5.25. Detectarea blob-urilor

Aplicarea pragului a transformat cadrul capturat într-o mască binară: fiecare pixel fie trece testul de prag, fie nu. Acest lucru răspunde la întrebarea ce culori care interesează aplicația apar în scenă, dar nu și la unde – masca este doar o mare de 1 și 0. Pasul următor este detectarea blob-urilor: parcurgerea măștii, găsirea regiunilor contigue de pixeli care trec testul și returnarea fiecăreia ca obiect cu o poziție, o dimensiune, o orientare și celelalte proprietăți asupra cărora o aplicație poate acționa.

find_blobs() este metoda de bază pentru acest pas și este cel mai comun punct de intrare în lumea obiectelor-rezultat ale modulului image. Urmărirea unei mingi colorate, urmărirea unei linii pictate pe podea, numărarea câtor puncte luminoase vede un senzor termic, decizia dacă un LED albastru este aprins sau stins – același apel le acoperă pe toate. Intrările se schimbă (pragurile, regiunea căutată, filtrele aplicate rezultatului), dar tiparul apelului este același.

5.25.1. Apelul de bază

find_blobs primește o listă de praguri și returnează o listă de obiecte-rezultat de tip 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))

Fiecare tuplu de prag are aceeași formă ca pragurile transmise către binary() – șase intrări (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) pentru o imagine RGB565 (limitele fiind în LAB), două intrări (lo, hi) pentru o imagine în tonuri de gri. Pot fi furnizate până la 32 de praguri într-un singur apel, ceea ce face ca find_blobs() să fie atât de flexibilă: balize roșii, verzi și albastre pot fi urmărite simultan, fiecare contribuind cu propriile blob-uri la lista returnată, iar proprietatea code a fiecărui blob identifică pragul cu care s-a potrivit.

Apelurile draw_rectangle() și draw_cross() de mai sus adnotează cadrul capturat pentru previzualizarea din IDE. Rezultatul blob conține deja b.rect (caseta de încadrare ca tuplu cu 4 elemente) și b.cx / b.cy (centroidul întreg), așa că desenarea detectării înapoi în cadru se reduce la două apeluri de metodă.

5.25.2. Ce conține rezultatul

Fiecare Blob este un tuplu cu atribute care reunește tot ceea ce a măsurat detectorul despre regiune. Proprietățile se împart în patru grupuri.

Grupul casetei de încadrare și al centroiduluix, y, w, h, rect, cx, cy, cxf, cyf – descrie poziția blob-ului. rect este tuplul cu 4 elemente (x, y, w, h) pe care metodele de desenare îl așteaptă; cx și cy sunt centroidul în coordonate de pixel întregi; cxf și cyf sunt centroidul în coordonate sub-pixel de tip float, utile când o calibrare din amonte are nevoie de poziții fracționare.

Descriptorii de formăpixels, area, density, perimeter, roundness, elongation, compactness, rotation – descriu cum arată blob-ul. pixels este numărul de pixeli care trec testul; area este aria casetei de încadrare aliniate cu axele (w * h); density este raportul dintre cele două, care se apropie de 1.0 pentru un dreptunghi plin și scade spre 0.0 pentru o linie diagonală subțire. roundness și compactness evaluează amândouă cât de rotund este blob-ul, din puncte de vedere geometrice diferite (roundness din momentele de ordinul al doilea, compactness din raportul perimetru-arie); elongation este 1.0 - roundness, pentru comoditate. rotation este orientarea axei majore în radiani, care este cea mai precisă pe blob-uri alungite și devine zgomotoasă pe cele aproape rotunde (o axă ambiguă nu are o direcție bine definită).

Metadatele de prag și de fuziunecode, count – identifică pragul care s-a potrivit și câte blob-uri sursă au fost fuzionate în cel returnat. code este o hartă de biți pe 32 de biți cu un bit setat pentru fiecare prag care se potrivește (un singur prag dă code == 1; un blob fuzionat multicolor poate avea mai mulți biți setați); count este 1 cu excepția cazului în care merge=True a combinat mai multe detectări într-una singură.

Grupul colțurilorcorners, min_corners – oferă geometria rotită a blob-ului. corners este tuplul cu 4 elemente de extreme (x, y) extrase din conturul blob-ului, sortate în sens orar începând din colțul stânga-sus; min_corners este tuplul cu 4 colțuri ale dreptunghiului rotit de arie minimă care încadrează blob-ul. Dreptunghiul de arie minimă este potrivirea strânsă; rect aliniat cu axele este potrivirea lejeră, aliniată cu grila de pixeli. Ambele sunt utile, în funcție de faptul dacă o etapă ulterioară are nevoie de o casetă orientată sau de una simplă.

O detectare de blob ilustrată pe fundalul unei măști binare de prag. Panoul din stânga arată o mască ovală înclinată de pixeli care trec testul. Panoul din dreapta arată aceeași mască adnotată cu caseta de încadrare aliniată cu axele desenată în jurul ei, cu centroidul marcat cu o cruce în mijloc, cu un dreptunghi rotit de arie minimă cu linie întreruptă strâns pe oval la unghiul său real și cu linia axei majore trecând prin centroid și orientată de-a lungul direcției lungi a ovalului.

Un blob conține caseta de încadrare aliniată cu axele (rect, x, y, w, h), centroidul (cx, cy sau sub-pixel cxf, cyf), dreptunghiul rotit de arie minimă (min_corners plus rotation) și liniile opționale ale axei majore / minore calculate de funcțiile-ajutor la nivel de modul prezentate mai jos.

5.25.4. Fuziunea blob-urilor suprapuse

merge=True post-procesează lista de rezultate pentru a combina blob-urile ale căror dreptunghiuri de încadrare se suprapun. Utilizarea naturală este detectarea unei ținte a cărei culoare camera o vede ca mai multe regiuni cu prag aplicat din cauza reflexiilor speculare, a liniilor de umbră sau a iluminării neuniforme pe obiect: o singură minge roșie ar putea fi returnată ca trei sau patru blob-uri roșii mici care, luate împreună, trasează mingea. Cu merge=True, cele trei blob-uri devin un blob mare, rect acoperă reuniunea, code este SAU pe biți al codurilor blob-urilor fuzionate (astfel încât o fuziune multicolor identifică ce culori au contribuit), iar count raportează câte blob-uri sursă au fost combinate.

margin mărește sau micșorează dreptunghiurile de încadrare înainte de testul de suprapunere. Cu margin=2, blob-urile ale căror dreptunghiuri de încadrare ajung la o distanță de 2 pixeli unul de altul se fuzionează totuși; cu margin=-2, se fuzionează doar blob-urile ale căror dreptunghiuri de încadrare se suprapun cu cel puțin 2 pixeli. Reglajul natural: margine pozitivă pentru a trata blob-urile pe care pragul le-a rupt în bucăți adiacente; margine negativă pentru a păstra separate obiectele distincte grupate strâns.

merge_cb rulează pe fiecare pereche candidată înainte ca fuziunea să aibă loc. Funcția de retroapelare primește cele două blob-uri și returnează True pentru a permite fuziunea sau False pentru a o împiedica. Acesta este instrumentul potrivit pentru verificarea încrucișată a fuziunilor pe care regula geometrică le ratează – de exemplu, refuzul de a fuziona două blob-uri ale căror unghiuri de rotation diferă cu mai mult decât un prag, sau refuzul de a fuziona un blob mic într-unul mult mai mare dacă cel mic este doar zgomot.

5.25.5. Histograme de proiecție

x_hist_bins_max și y_hist_bins_max atașează histograme de proiecție opționale fiecărui blob. O histogramă de proiecție este numărul de pixeli care trec testul de-a lungul unei axe: histograma axei X totalizează pixelii care trec testul pe coloană în interiorul casetei de încadrare a blob-ului, iar histograma axei Y totalizează pe rând. Ambele au valoarea implicită zero – histogramele nu sunt calculate decât dacă se furnizează un max diferit de zero, deoarece altfel ar adăuga muncă fiecărei detectări.

Când sunt calculate, histogramele oferă un semnal 1-D ieftin pe care o aplicație poate rula analize ulterioare: detectarea poziției unei dungi verticale în interiorul blob-ului, găsirea punctului de tranziție al unei ținte bicolore, numărarea câtor goluri apar de-a lungul axei lungi. Ele sunt populate ca proprietățile x_hist_bins și y_hist_bins pe fiecare Blob.

5.25.6. Funcții-ajutor geometrice suplimentare

Câteva măsuri geometrice suplimentare există ca funcții la nivel de modul, care primesc un blob și returnează măsurătoarea solicitată:

  • image.get_solidity() returnează soliditatea blob-ului – pixelii împărțiți la aria înfășurătorii convexe. O regiune plină și solidă este aproape de 1.0; un blob cu concavități (o potcoavă, o mână cu degetele răsfirate) scade mult sub.

  • image.get_convexity() returnează convexitatea – perimetrul înfășurătorii convexe împărțit la perimetrul blob-ului. Un blob perfect convex este 1.0; blob-urile zimțate sau crestate sunt mai mici.

  • image.get_major_axis_line() și image.get_minor_axis_line() returnează obiecte Line de-a lungul axelor majore și minore ale blob-ului, derivate din dreptunghiul rotit de arie minimă.

  • image.get_enclosing_circle() returnează un Circle care încadrează blob-ul – util când o etapă ulterioară are nevoie de un cerc de desenat sau de testat.

  • image.get_enclosed_ellipse() returnează tuplul cu 5 elemente (cx, cy, rx, ry, rotation) pentru o elipsă înscrisă în dreptunghiul de arie minimă al blob-ului. Valorile sunt transmise direct către draw_ellipse().

5.25.7. Învățarea automată a unui prag

Un detector de blob-uri este la fel de bun ca pragurile cu care este rulat, iar munca de găsire a pragului corect pentru o culoare-țintă este o problemă în sine. Două tipare comune reduc această muncă.

Primul este selecția interactivă în IDE: capturezi un cadru, tragi un dreptunghi în jurul unui exemplu de culoare-țintă și lași editorul de praguri al IDE-ului să raporteze limitele LAB pe care le vede. Acele limite ajung în script ca praguri pentru find_blobs(), iar detectorul este gata.

Al doilea este învățarea automată programatică: o rutină de calibrare care rulează pe cameră capturează un cadru, ia o histogramă a unei zone cunoscute în care se află ținta (get_histogram() cu roi=) și citește intervalul de valori al zonei din histogramă cu get_percentile(). Percentila a 5-a setează limita inferioară a fiecărui canal, iar a 95-a limita superioară, ignorând pixelii aberanți de la ambele extreme. Pe o imagine RGB565, un singur apel de percentilă raportează toate cele trei canale LAB deodată, astfel încât cele două apeluri produc cele șase numere pe care find_blobs() le așteaptă:

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)