martedì 27 giugno 2023

Confronto ArucoTags ed AprilTags

 Ho provato una comparazione sulla stabilita' della misura di distanza usando Aruco Tags (con Opencv) e AprilTags (con AprilTag v2 e v3 modificata per avere la pose estimation)

Sono state acquisite 576 immagini con una camera Reolink E1 Pro (con immagini sia notturne che diurne) di due tag delle medesime dimensioni (4x4_100 per Aruco e 36h11 per Apriltag 10 cm)

Le nuove versioni di OpenCV hanno modificate le API per cui gli script che ho usato l'anno scorso non funzionano piu'. Per fare prima ho usato il docker sottostante che monta OpenCV 4.5.5

docker run -it -v /home/luca/ottico/:/home/ottichdgigante/python-opencv:4.5.5-debian  sh




OpenCV ed Aruco mostrano outliers, Apriltag3 ne mostra solo uno mentre Apriltag 2 ha mostrato dati molto piu' stabili)




Rimossi gli outlier si evidenzia che l'errore quadratico medio di Arpriltag e' circa la meta' di quella di Aruco (l'errore quadratico  migliore e' circa lo 0,3 % della distanza)






venerdì 16 giugno 2023

Restful con resty in Go

Attenzione : necesssita' di GO 1.20. In versioni precedenti viene generato l'errore value.SetZero undefined


La libreria Resty github.com/go-resty/resty/ permette di utilizzare servizi restful anche dietro a proxy e con connessione insecure (connessione https con certificato scaduto) 

=========================================================

package main
import (
"crypto/tls"
"fmt"
"os"
"github.com/go-resty/resty/v2"   // libreria per rest
)
type AuthSuccess struct {
success      string
version      string
JWT          string
username     string
userid       string
parentuserid string
userlevelid  string
}
func dieOnError(msg string, err error) {
if err != nil {
fmt.Println(msg, err)
os.Exit(1)
}
}
// go get github.com/go-resty/resty/v2
func main() {
connStr := "connessione"
conn, err := go_ora.NewConnection(connStr)
dieOnError("Can't create connection:", err)
err = conn.Open()
dieOnError("Can't open connection:", err)
client := resty.New()
client.SetProxy("http://proxy.xxxxx.toscana.it:8080")
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
// User Login
resp, err := client.R().
SetFormData(map[string]string{
"username": "admin",
"password": "xxxxxxx",
"action":   "login",
}).
SetResult(&AuthSuccess{}).
Post("https://xxxxxx.xxxxxxx.it/api/")
if err != nil {
fmt.Println("Errore di login")
os.Exit(1)
}
token := resp.Result().(*AuthSuccess).JWT
fmt.Print(token)
}

Connession Oracle DB con Go

Per provare a connettere Oracle tramite Go torna utile utilizzare il seguente docker

docker run -d -p 1521:1521 -e ORACLE_PASSWORD=<your password> -v oracle-volume:/opt/oracle/oradata gvenzl/oracle-xe

l'utente e' SYSTEM e service XEPDB1

Per interagire con il db si puo' usare SQLDeveloper  

Con la libreria go-ora non e' necessario installare le librerie client di Oracle


package main

import (
    "database/sql"
    "fmt"

    _ "github.com/sijms/go-ora/v2"
)

func sqlOperations(db *sql.DB) {
    var queryResultColumnOne string
    row := db.QueryRow("SELECT punto_cantiere FROM nodo")
    err := row.Scan(&queryResultColumnOne)
    if err != nil {
        panic(fmt.Errorf("error scanning query result from database into target variable: %w", err))
    }
    fmt.Println("Risultato query", queryResultColumnOne)

    rows, err := db.Query("SELECT punto_cantiere,valore FROM nodo")
    if err != nil {
        // handle this error better than this
        panic(err)
    }
    defer rows.Close()

    for rows.Next() {
        var numero float32
        var nome string
        err = rows.Scan(&nome, &numero)
        if err != nil {
            // handle this error
            panic(err)
        }
        fmt.Println(numero, nome)
    }
    // get any error encountered during iteration
    err = rows.Err()
    if err != nil {
        panic(err)
    }

}

func GetSqlDBWithPureDriver(dbParams map[string]string) *sql.DB {
    connectionString := "oracle://" + dbParams["username"] + ":" +
        dbParams["password"] + "@" + dbParams["server"] + ":" +
        dbParams["port"] + "/" + dbParams["service"]
    db, err := sql.Open("oracle", connectionString)
    if err != nil {
        panic(fmt.Errorf("error in sql.Open: %w", err))
    }
    err = db.Ping()
    if err != nil {
        panic(fmt.Errorf("error pinging db: %w", err))
    }
    return db
}

var localDB = map[string]string{
    "service":  "XEPDB1",
    "username": "system",
    "server":   "localhost",
    "port":     "1521",
    "password": "password",
}

func main() {
    db := GetSqlDBWithPureDriver(localDB)
    defer func() {
        err := db.Close()
        if err != nil {
            fmt.Println("Can't close connection: ", err)
        }
    }()
    sqlOperations(db)
}

mercoledì 14 giugno 2023

Aruco calibration file format

 Ho provato a vedere se effettuare la calibrazione di tag Aruco con immagini in formato raw lossless od in formato jpg (lossy) poteva influenzare l'errore finale




Per fare cio' ho preso un telefono Android che permette di salvare la stessa immagine in formato DNG ed in formato JPG 

Lo script di calcolo e' il seguente

import numpy as np
import cv2
import cv2.aruco as aruco

# Aruco parameters
aruco_dict = aruco.Dictionary_get(aruco.DICT_4X4_50)
aruco_params = aruco.DetectorParameters_create()

# Chessboard parameters
num_chessboard_corners_horizontal = 9
num_chessboard_corners_vertical = 6
chessboard_square_size = 0.025  # in meters

# Image directory and file extension
image_directory = 'path_to_images_directory/'
image_extension = '.jpg'

# Arrays to store object points and image points
obj_points = []  # 3D points in real-world coordinates
img_points = []  # 2D points in image plane

# Generate chessboard object points
chessboard_size = (num_chessboard_corners_horizontal,
num_chessboard_corners_vertical)
objp = np.zeros((num_chessboard_corners_horizontal *
num_chessboard_corners_vertical, 3), np.float32)
objp[:, :2] = np.mgrid[0:num_chessboard_corners_horizontal,
0:num_chessboard_corners_vertical].T.reshape(-1, 2)
objp = objp * chessboard_square_size

# Iterate through images and find chessboard corners
image_count = 0
while True:
    # Load image
    image_path = image_directory + str(image_count) + image_extension
    image = cv2.imread(image_path)
    if image is None:
        break

    # Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Find chessboard corners
    ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)

    # If corners found, add object points and image points
    if ret == True:
        obj_points.append(objp)
        img_points.append(corners)

        # Draw and display corners
        cv2.drawChessboardCorners(image, chessboard_size, corners, ret)
        cv2.imshow('Chessboard Corners', image)
        cv2.waitKey(500)

    image_count += 1

cv2.destroyAllWindows()

# Perform Aruco calibration
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points,
img_points, gray.shape[::-1], None, None)

# Calculate reprojection error
mean_error = 0
for i in range(len(obj_points)):
    img_points_proj, _ = cv2.projectPoints(obj_points[i], rvecs[i],
tvecs[i], mtx, dist)
    error = cv2.norm(img_points[i], img_points_proj, cv2.NORM_L2) /
len(img_points_proj)
    mean_error += error

calibration_error = mean_error / len(obj_points)
print("Calibration error: {}".format(calibration_error))

# Estimate additional error
reproj_error = 0
total_points = 0
for i in range(len(obj_points)):
    img_points_proj, _ = cv2.projectPoints(obj_points[i], rvecs[i],
tvecs[i], mtx, dist)
    error = cv2.norm(img_points[i], img_points_proj, cv2.NORM_L2)
    reproj_error += error
    total_points += len(obj_points[i])

additional_error = reproj_error / total_points
print("Additional error: {}".format(additional_error))

I risultati sono i seguenti (per i file DNG ho usato la libreria rawpy)

JPG

python calibration.py --dir ./jpg/ --square_size 0.015  -w 9 -t 6
Calibration error: 0.13602731796497697
Additional error: 0.13602731796497697

DNG

Calibration error: 0.13606447706803765

Additional error: 0.13606447706803765

In conclusione non si ha una significativa differenza di errore nell'usare JPG o DNG

giovedì 1 giugno 2023

Match Histogram

Un esempio per normalizzare l'istogramma tra due immagini riprese a differente orari con ovvie differenze di illuminazione mediante la funzione match_histogram di Scikit (attenzione che nelle versioni precedenti la funzione era definita in modo differente per i parametri)


Immagine corretta. Si usa la prima come reference e la seconda come immagine da correggere


 Questi gli istogrammi prima e dopo l'algoritmo


from skimage import exposure
import matplotlib.pyplot as plt
import argparse
import cv2
ap = argparse.ArgumentParser()
ap.add_argument("-s", "--source", required=True,
help="path to the input source image")
ap.add_argument("-r", "--reference", required=True,
help="path to the input reference image")
args = vars(ap.parse_args())

print("[INFO] loading source and reference images...")
src = cv2.imread(args["source"])
ref = cv2.imread(args["reference"])
print("[INFO] performing histogram matching...")
multi = True if src.shape[-1] > 1 else False
matched = exposure.match_histograms(src, ref, channel_axis=-1)
cv2.imshow("Source", src)
cv2.imshow("Reference", ref)
cv2.imshow("Matched", matched)
cv2.imwrite("matched.jpg",matched)
cv2.waitKey(0)

(fig, axs) = plt.subplots(nrows=3, ncols=3, figsize=(8, 8))
for (i, image) in enumerate((src, ref, matched)):
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
for (j, color) in enumerate(("red", "green", "blue")):
(hist, bins) = exposure.histogram(image[..., j],
source_range="dtype")
axs[j, i].plot(bins, hist / hist.max())
(cdf, bins) = exposure.cumulative_distribution(image[..., j])
axs[j, i].plot(bins, cdf)
axs[j, 0].set_ylabel(color)

axs[0, 0].set_title("Source")
axs[0, 1].set_title("Reference")
axs[0, 2].set_title("Matched")
plt.tight_layout()
plt.savefig("grafico.png")

 

Eth0 su LuckFox Pico Mini A

 Le schede Luckfox Pico Mini A (a differenza delle sorelle maggiori) non hanno un connettore RJ45 e nonostante i pin da saldare non sembrano...