Il problema e' il seguente: trovare le differenze tra le due foto. Le due immagini sono state riprese a distanza di oltre un anno ed il punto di ripresa non e' esattamente il solito per cui c'e' un effetto di traslazione e rotazione seppure non immediatamente
Prima
Dopo
Con lo script sottostante sono state estratte le feature tramite AKAZE e successivamente sono state matchate e filtrate in base alla distanza tramite FLANN
E' stata calcolata la distanza in pixel tra il punto nella prima immagine ed il suo omologo nella seconda sottraendo un valore costante derivante (1000) e colorando il punto secondo la scala colore da verde a rosso a secondo dell'intensita' di movimento
import cv2 as cv import matplotlib.pyplot as plt import numpy as np import math
# Find the keypoints and compute the descriptors for input and training-set image keypoints1, descriptors1 = AKAZE.detectAndCompute(image1, None) keypoints2, descriptors2 = AKAZE.detectAndCompute(image2, None)
Uno dei problemi maggiori dell'uso dei tag aruco in esterno per misurare distanze e' che le condizioni di illuminazione solare sono molto variabili e la compressione jpg completa l'opera aggiungendo rumore su rumore
Per cercare di limitare il problema ho provato a fare image stacking sovrapponendo 8 foto (una ogni ora dalle 09 alle 16) con lo script al link https://github.com/maitek/image_stacking ed applicando il programma visto qui
Il miglioramento non e' trascurabile perche' la standard deviation e' passata
3.1 m => da 0.19% a 0.16% (0.5 cm)
5.4 m => da 0.35% a 0.28% (1.5 cm)
7.6 m => da 0.71% a 0.38% (2.8 cm)
9.6 m => da 0.5% a 0.41% (4.8 cm)
import os import cv2 import numpy as np from time import time
# Align and stack images with ECC method # Slower but more accurate def stackImagesECC(file_list): M = np.eye(3, 3, dtype=np.float32)
first_image = None stacked_image = None for file in file_list: image = cv2.imread(file,1).astype(np.float32) / 255 print(file) if first_image is None: # convert to gray scale floating point image first_image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) stacked_image = image else: # Estimate perspective transform s, M = cv2.findTransformECC(cv2.cvtColor(image,cv2.COLOR_BGR2GRAY), first_image, M, cv2.MOTION_HOMOGRAPHY) w, h, _ = image.shape # Align image to first image image = cv2.warpPerspective(image, M, (h, w)) stacked_image += image
if first_image is None: # Save keypoints for first image stacked_image = imageF first_image = image first_kp = kp first_des = des else: # Find matches and sort them in the order of their distance matches = matcher.match(first_des, des) matches = sorted(matches, key=lambda x: x.distance)
src_pts = np.float32( [first_kp[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2) dst_pts = np.float32( [kp[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
Ho avuto la fortuna di poter usare una enorma Charuco Board legata al cane robot Spot della Boston Dynamics
Nonostante non sia esplicitamente indicato si tratta di una Charuco Board 4x4_100 di 9 colonne e 4 righe
Per usarla per calibrare una camera si puo' usare il seguente script OpenCV
Attenzione: per funzionare lo script necessita setLegacyPattern(True)
import os import numpy as np import cv2
# ------------------------------ # ENTER YOUR REQUIREMENTS HERE: ARUCO_DICT = cv2.aruco.DICT_4X4_250 SQUARES_VERTICALLY = 9 SQUARES_HORIZONTALLY = 4 SQUARE_LENGTH = 0.115 MARKER_LENGTH = 0.09 # ... PATH_TO_YOUR_IMAGES = './hikvision' # ------------------------------ def calibrate_and_save_parameters(): # Define the aruco dictionary and charuco board dictionary = cv2.aruco.getPredefinedDictionary(ARUCO_DICT) board = cv2.aruco.CharucoBoard((SQUARES_VERTICALLY, SQUARES_HORIZONTALLY), SQUARE_LENGTH, MARKER_LENGTH, dictionary) board.setLegacyPattern(True) params = cv2.aruco.DetectorParameters() params.cornerRefinementMethod = 0 image_files = [os.path.join(PATH_TO_YOUR_IMAGES, f) for f in os.listdir(PATH_TO_YOUR_IMAGES) if f.endswith(".jpg")] image_files.sort()
all_charuco_corners = [] all_charuco_ids = []
for image_file in image_files: print(image_file) image = cv2.imread(image_file) marker_corners, marker_ids, _ = cv2.aruco.detectMarkers(image, dictionary, parameters=params)
# If at least one marker is detected if len(marker_ids) > 0: charuco_retval, charuco_corners, charuco_ids = cv2.aruco.interpolateCornersCharuco(marker_corners, marker_ids, image, board)
if charuco_retval: all_charuco_corners.append(charuco_corners) all_charuco_ids.append(charuco_ids)
Ho riscritto per la nuova versione di OpenCV il programma di estrazione dei dati degli Aruco Tag
Il programma e' completamente parametrizzato (con valori di default) ed ha un output in csv in cui sono inserite le coordinate 2D immagine, coordinate 3D nel sistema di riferimento della camera, angoli di roll,pitch ed yaw
from os import listdir from os.path import isfile, join
import numpy as np import argparse import cv2 import sys import math import os
ap = argparse.ArgumentParser() #mettere rem sulla successiva per utilizzare il ciclo sulle img #ap.add_argument("-i", "--image", required=True, help="path to input image containing ArUCo tag") ap.add_argument("-t", "--type", type=str, default="DICT_4X4_250", help="type of ArUCo tag to detect") ap.add_argument("-k", "--K_Matrix", type=str,default='./calibration_matrix.npy',help="Path to calibration matrix (numpy file)") ap.add_argument("-d", "--D_Coeff", type=str,default='./distortion_coefficients.npy',help="Path to distortion coefficients (numpy file)") ap.add_argument("-a", "--aruco_dim", default=25,type=int, help="ArUco tag dimension") ap.add_argument("-g", "--tag", default=0, type=str, help="Select only one Id") ap.add_argument("-p", "--path", default="./", help="Path folder immagini")
#la dimensione del tag e' quella dello spigolo esterno del quadrato nero esterno, non i singoli quadrati interni args = vars(ap.parse_args()) if ARUCO_DICT.get(args["type"], None) is None: print(f"ArUCo tag type '{args['type']}' is not supported") sys.exit(0)
calibration_matrix_path = args["K_Matrix"] distortion_coefficients_path = args["D_Coeff"] k = np.load(calibration_matrix_path) d = np.load(distortion_coefficients_path)
immagini = [f for f in listdir(args["path"]) if isfile(join(args["path"], f))]
with open('aruco'+str(args["tag"])+'.csv', 'w') as f: f.write("Id;Xpix;Ypix;X;Y;Z;Filename;Roll;Pitch;Roll\n") for i in immagini: print(args["path"]+i) estrai_parametri(args["path"]+i) f.close()
Ho scoperto per caso che le camere UVC sono facilmente controllabili da OpenCV in particolare per quanto riguardo il parametro dell'auto esposizione
Per le caratteristiche della camera si usa
v4l2-ctl --list-formats-ext -d 0
con il parametro d che e' il numero del device
===============================================
import cv2
cap = cv2.VideoCapture(0)
# The control range can be viewed through v4l2-ctl -L cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080) cap.set(cv2.CAP_PROP_BRIGHTNESS, 64) cap.set(cv2.CAP_PROP_CONTRAST, 0) cap.set(cv2.CAP_PROP_EXPOSURE, 100) #in Linux l'esposizione e' 1/n #cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1) #controlla autoesposizione 1=true
parser = argparse.ArgumentParser(description='Code for Shi-Tomasi corner detector tutorial.')
parser.add_argument('--input', help='Path to input image.', default='pic3.png')
args = parser.parse_args()
src = cv.imread(cv.samples.findFile(args.input))
if src isNone:
print('Could not open or find the image:', args.input)
exit(0)
src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
maxCorners =10# initial threshold
goodFeaturesToTrack_Demo(maxCorners)
print()
il risultato dell'algotitmo e' di questo tipo
Alla fine e' emerso l'algoritmo seleziona in modo automatico gli angoli con un ordine diverso e c'e' una forte influenza dell'illuminazione sulla definizione delle coordinate dell'angolo
In casi ottimali (ma non e' questo il caso) ho visto che la standard deviation e' di circa 0.2 pixels
Non e' quindi un metodo affidabile per il change detection
Dopo aver provato un po' di tutto per correggere i dati ho scoperto che le immagini originali non sono state riprese in modo corretto. La camera satura in alcuni condizioni di luce come si vede dai due esempi sottostanti rendendo inutile l'elaborazione
Prova comparativa per misuare distanze tramite April ed Aruco tag in condizioni reali
Lo scopo e' quello di verificare la ripetibilita' delle misure di distanza mediante tag a condizioni di luce variabile e per questo sono state effettuate misure con i tag in posizione stazionaria
E' stata impiegata una camera di sorveglianza a fuoco fisso con acquisizione ogni 10 minuti anche di notte grazie all'illuminazione ad infrarossi
I tag sono stati di dimensione di 25 cm in formato 4x4 per Aruco e 36h11 per gli Apriltag
Per determinare la distanza sono state impiegate le librerie OpenCV su Python per agli Aruco Tags mentre la libreria Apriltags3 in C++
in generale gli Apriltag risultano meglio individuabili rispetto agli Aruco tag. Di 430 immagini totali gli Apriltags sono stati individuati al 99% ad una distanza di 6 m mentre gli Aruco tag hanno prestazioni simili ma solo sulla breve distanza (4 m)
Aruco
l'elaborazione dei tag aruco indica che l'algoritmo genera molti outliers che possono essere facilmente separati dai dati corretti
Provando a smussare i dati con una media mobile a finetra oraria la situazione non migliora e si osserva un comportamento legato all'illuminazione che si ritrovera' anche dopo con gli April Tags
Apriltag
Dall'analisi dei grafici si vede che le condizioni di illuminazione condizionano fortemente la misura della distanza mediante Apriltag. Le misure piu' stabili sono di notte quando e' attiva l'illuminazione dei led ad infrarossi
Rispetto ad Aruco ci sono molti meno outliers ed i dati sono meno rumorosi
In queste condizioni e' stato registrato un errore di standard devitation pari a 0.96% della distanza per il tag a 6.5m e dell'1% per il tag a 10 m
Se si plottano i dati a parita' di ora del giorno si vede ancora piu' chiaramente come la presenza di ombra influenza il dato di distanza
Se si fa la differenza tra le due curve l'errore scende al 0.18%
Un sistema per rimuovere l'effetto dell'illuminazione e' di correlare i dati dei due tag (sono vicini quindi sono illuminati in modo comparabile)
Per cercare di risolvere il problema delle differenti illuminazione ho provato ad elaborare le immagini mediante l'algoritmo presentato in questo articolo (Illumination Invariant Imaging: Applications in Robust Vision-based
Localisation, Mapping and Classification for Autonomous Vehicles)
In estrema sintesi l'algoritmo appiattisce una immagine RGB e cerca di annullare gli effetti di differente illuminaizone (questo algoritmo funziona solo sulle immagini diurne perche' la camera di notte acquisisce a scala di grigi)
Per le elaborazioni ho usato questo progetto su Github. (ho dovuto fare una leggere modifica perche' le immagini della camera avevano dei valori zero che ovviamente non potevano essere usati in un logaritmo)
dopo l'applicazione della elaborazione l'algoritmo di riconoscimento dei tag risulta molto piu' in difficolta' nel riconoscere i taf e sono state estratte solo 71 misure di distanza del tag1 e 16 misure del tag 2
Aggiornamento:
Frugando dentro al codice della demo di Apritag3 c'e' un porzione di codice che non puo' mai essere eseguito (c'e' un IF sempre True) e la condizione Falsa e' appunto l'algoritmo di Illumination Invariant
Basta modificare la riga 156 per esempio aggiungendo un NOT si introduce il calcolo
questi sono i grafici risultanti dopo l'algoritmo. Si e' oersa la ritmicita' dell'illuminazione ma si e' persa anche la capacita' di riconoscere i tag nelle immagini trattate (per il tag 1 circa il 50%, tag2 decisamente peggio)
L'errore percentuale delle standard deviation e' pari a 1.79% per il tag1 e 1.08% per il tag 2
La differenza risiede nel valore del parametro utilizzato nell'elaborazione delle immagini
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/ottico hdgigante/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)