5.25. Ricerca dei blob

L’applicazione della soglia ha trasformato il frame catturato in una maschera binaria: ogni pixel supera il test di soglia oppure no. Questo risponde alla domanda su quali colori che interessano all’applicazione compaiono nella scena, ma non dove – la maschera è solo un mare di 1 e 0. Il passo successivo è il rilevamento dei blob: percorrere la maschera, trovare regioni contigue di pixel che superano la soglia e restituire ognuna come un oggetto con una posizione, una dimensione, un orientamento e le altre proprietà su cui un’applicazione può agire.

find_blobs() è il metodo di riferimento per questo passo ed è il punto di accesso più comune al mondo degli oggetti-risultato del modulo image. Tracciare una pallina colorata, seguire una linea dipinta sul pavimento, contare quanti punti luminosi vede un sensore termico, decidere se un LED blu è acceso o spento – la stessa chiamata copre tutti questi casi. Gli ingressi cambiano (le soglie, la regione cercata, i filtri applicati al risultato), ma lo schema della chiamata è lo stesso.

5.25.1. La chiamata di base

find_blobs prende una lista di soglie e restituisce una lista di oggetti-risultato blob:

thresholds = [(30, 100, 15, 127, 15, 127)]  # LAB threshold for red
blobs = img.find_blobs(thresholds)

for b in blobs:
    img.draw_rectangle(b.rect, color=(255, 0, 0))
    img.draw_cross(b.cx, b.cy, color=(255, 0, 0))

Ogni tupla di soglia ha la stessa forma delle soglie passate a binary() – sei voci (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) per un’immagine RGB565 (i limiti sono in LAB), due voci (lo, hi) per un’immagine in scala di grigi. In una singola chiamata si possono fornire fino a 32 soglie, ed è questo che rende find_blobs() così flessibile: beacon rossi, verdi e blu possono essere tracciati simultaneamente, ognuno contribuendo con i propri blob alla lista restituita, e la proprietà code di ogni blob identifica quale soglia ha trovato corrispondenza.

Le chiamate draw_rectangle() e draw_cross() qui sopra annotano il frame catturato per l’anteprima nell’IDE. L’oggetto-risultato blob contiene già b.rect (il bounding box come tupla di 4 elementi) e b.cx / b.cy (il centroide intero), quindi ridisegnare il rilevamento nel frame richiede due chiamate di metodo.

5.25.2. Cosa contiene il risultato

Ogni Blob è una tupla con attributi che raccoglie tutto ciò che il rilevatore ha misurato della regione. Le proprietà si dividono in quattro gruppi.

Il gruppo bounding box e centroidex, y, w, h, rect, cx, cy, cxf, cyf – descrive la posizione del blob. rect è la tupla di 4 elementi (x, y, w, h) che i metodi di disegno si aspettano; cx e cy sono il centroide in coordinate intere di pixel; cxf e cyf sono il centroide in coordinate float sub-pixel, utili quando una calibrazione a monte tiene conto di posizioni frazionarie.

Il gruppo descrittori di formapixels, area, density, perimeter, roundness, elongation, compactness, rotation – descrive l’aspetto del blob. pixels è il conteggio dei pixel che superano la soglia; area è l’area del bounding box allineato agli assi (w * h); density è il rapporto tra i due, che si avvicina a 1.0 per un rettangolo pieno e scende verso 0.0 per un tratto diagonale sottile. roundness e compactness valutano entrambi quanto il blob sia rotondo, da punti di vista geometrici diversi (roundness dai momenti del secondo ordine, compactness dal rapporto perimetro-area); elongation è 1.0 - roundness per comodità. rotation è l’orientamento dell’asse maggiore in radianti, che è più accurato sui blob allungati e diventa rumoroso su quelli quasi rotondi (un asse ambiguo non ha una direzione ben definita).

Il gruppo metadati di soglia e fusionecode, count – identifica quale soglia ha trovato corrispondenza e quanti blob di origine sono stati fusi in quello restituito. code è una bitmap a 32 bit con un bit impostato per ogni soglia corrispondente (una singola soglia dà code == 1; un blob multicolore fuso può avere più bit impostati); count è 1 a meno che merge=True non abbia combinato diversi rilevamenti in uno solo.

Il gruppo angolicorners, min_corners – fornisce la geometria ruotata del blob. corners è la tupla di 4 elementi di estremi (x, y) ricavati dal contorno del blob, ordinati in senso orario a partire dall’angolo in alto a sinistra; min_corners è la tupla di 4 angoli del rettangolo ruotato di area minima che racchiude il blob. Il rettangolo di area minima è l’adattamento stretto; il rect allineato agli assi è l’adattamento lasco allineato alla griglia di pixel. Entrambi sono utili a seconda che uno stadio a valle necessiti di un box orientato o di uno semplice.

Un rilevamento di blob illustrato su una maschera di soglia binaria. Il pannello di sinistra mostra una maschera ovale inclinata di pixel che superano la soglia. Il pannello di destra mostra la stessa maschera annotata con il bounding box allineato agli assi disegnato attorno ad essa, il centroide segnato con una croce al centro, un rettangolo ruotato di area minima tratteggiato che avvolge l'ovale alla sua reale angolazione e la linea dell'asse maggiore che passa per il centroide puntando lungo la direzione del lato lungo dell'ovale.

Un blob contiene il bounding box allineato agli assi (rect, x, y, w, h), il centroide (cx, cy o i sub-pixel cxf, cyf), il rettangolo ruotato di area minima (min_corners più rotation) e le linee opzionali dell’asse maggiore / minore calcolate dalle funzioni di supporto a livello di modulo descritte più avanti.

5.25.4. Fondere blob sovrapposti

merge=True post-elabora la lista dei risultati per combinare i blob i cui rettangoli di delimitazione si sovrappongono. L’uso naturale è rilevare un bersaglio il cui colore la camera vede come più regioni con soglia a causa di riflessi speculari, linee d’ombra o illuminazione non uniforme sull’oggetto: una singola palla rossa potrebbe tornare come tre o quattro piccoli blob rossi che, presi insieme, tracciano la palla. Con merge=True, i tre blob diventano un unico grande blob, il rect copre l’unione, il code è l’OR bit a bit dei code dei blob fusi (così una fusione multicolore identifica quali colori hanno contribuito) e count riporta quanti blob di origine sono stati combinati.

margin ingrandisce o riduce i rettangoli di delimitazione prima del test di sovrapposizione. Con margin=2, i blob i cui rettangoli di delimitazione arrivano entro 2 pixel l’uno dall’altro si fondono comunque; con margin=-2, si fondono solo i blob i cui rettangoli di delimitazione si sovrappongono di almeno 2 pixel. La regolazione naturale: margine positivo per gestire blob che la soglia ha spezzato in parti adiacenti; margine negativo per mantenere separati oggetti distinti ravvicinati.

merge_cb viene eseguito su ogni coppia candidata prima che avvenga la fusione. Il callback riceve i due blob e restituisce True per consentire la fusione o False per impedirla. Questo è lo strumento giusto per i controlli incrociati sulle fusioni che la regola geometrica non coglie – ad esempio, rifiutare di fondere due blob i cui angoli di rotation differiscono di oltre una soglia, o rifiutare di fondere un piccolo blob in uno molto più grande se quello piccolo è solo granulosità.

5.25.5. Istogrammi di proiezione

x_hist_bins_max e y_hist_bins_max allegano istogrammi di proiezione opzionali a ogni blob. Un istogramma di proiezione è il conteggio dei pixel che superano la soglia lungo un asse: l’istogramma dell’asse X somma i pixel che superano la soglia per colonna all’interno del bounding box del blob, e l’istogramma dell’asse Y somma per riga. Entrambi hanno valore predefinito zero – gli istogrammi non vengono calcolati a meno che non venga fornito un max diverso da zero, poiché altrimenti aggiungerebbero lavoro a ogni rilevamento.

Quando vengono calcolati, gli istogrammi forniscono un segnale monodimensionale economico su cui un’applicazione può eseguire ulteriori analisi: rilevare la posizione di una striscia verticale all’interno del blob, trovare il punto di rottura di un bersaglio a due colori, contare quanti vuoti compaiono lungo l’asse lungo. Vengono popolati come le proprietà x_hist_bins e y_hist_bins su ogni Blob.

5.25.6. Ulteriori funzioni di supporto geometrico

Una manciata di ulteriori misure geometriche esistono come funzioni a livello di modulo che prendono un blob e restituiscono la misura richiesta:

  • image.get_solidity() restituisce la solidità del blob – i pixel divisi per l’area dell’inviluppo convesso. Una regione piena solida è vicina a 1.0; un blob con concavità (un ferro di cavallo, una mano con le dita aperte) scende ben al di sotto.

  • image.get_convexity() restituisce la convessità – il perimetro dell’inviluppo convesso diviso per il perimetro del blob. Un blob perfettamente convesso vale 1.0; blob frastagliati o intaccati hanno valori inferiori.

  • image.get_major_axis_line() e image.get_minor_axis_line() restituiscono oggetti Line lungo gli assi maggiore e minore del blob, derivati dal rettangolo ruotato di area minima.

  • image.get_enclosing_circle() restituisce un Circle che racchiude il blob – utile quando uno stadio a valle vuole un cerchio da disegnare o con cui confrontarsi.

  • image.get_enclosed_ellipse() restituisce la tupla di 5 elementi (cx, cy, rx, ry, rotation) per un’ellisse inscritta nel rettangolo di area minima del blob. I valori si inseriscono direttamente in draw_ellipse().

5.25.7. Apprendimento automatico di una soglia

Un rilevatore di blob è valido tanto quanto le soglie con cui viene eseguito, e il lavoro di trovare la soglia giusta per un colore bersaglio è un problema a sé. Due schemi comuni riducono questo lavoro.

Il primo è la selezione interattiva nell’IDE: catturare un frame, trascinare un rettangolo attorno a un esempio del colore bersaglio e lasciare che l”editor di soglia dell’IDE riporti i limiti LAB che rileva. Quei limiti finiscono nello script come soglie di find_blobs() e il rilevatore è pronto.

Il secondo è l’apprendimento automatico programmatico: una routine di calibrazione in esecuzione sulla camera cattura un frame, prende un istogramma di una zona nota in cui si trova il bersaglio (get_histogram() con roi=) e legge l’intervallo di valori della zona dall’istogramma con get_percentile(). Il 5° percentile imposta il limite inferiore di ogni canale e il 95° quello superiore, ignorando i pixel anomali sparsi a entrambe le estremità. Su un’immagine RGB565 una chiamata di percentile riporta tutti e tre i canali LAB contemporaneamente, quindi le due chiamate producono i sei numeri che find_blobs() si aspetta:

h = img.get_histogram(roi=patch)
lo = h.get_percentile(0.05)
hi = h.get_percentile(0.95)
threshold = (lo.l_value, hi.l_value,
             lo.a_value, hi.a_value,
             lo.b_value, hi.b_value)