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 centroidului – x, 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 fuziune – code, 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țurilor – corners, 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ă.
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.3. Filtrarea căutării¶
Un cadru capturat conține de obicei pixeli care se potrivesc cu pragul din alte motive decât obiectul care interesează aplicația: reflexii speculare, obiecte de fundal îndepărtate, pixeli de zgomot din imagine care se nimeresc în intervalul LAB. Argumentele cuvânt-cheie ale find_blobs() sunt prima linie de apărare.
roi restrânge căutarea la o regiune a cadrului, la fel ca orice altă metodă a modulului image. O aplicație care știe că obiectul poate apărea doar în jumătatea inferioară a câmpului vizual transmite roi=(0, h//2, w, h//2) și ignoră tot ce este deasupra; timpul economisit se transformă înapoi în rată de cadre.
area_threshold și pixels_threshold filtrează amândouă blob-urile care sunt prea mici pentru a conta. area_threshold elimină blob-urile a căror casetă de încadrare are mai puțini pixeli de arie decât valoarea respectivă (util pentru filtrarea zgomotului împrăștiat); pixels_threshold elimină blob-urile care au mai puțini pixeli care trec testul decât valoarea respectivă (util pentru filtrarea blob-urilor care sunt mari, dar rare, cum ar fi un model de punctare cu prag în care se potrivesc unul sau doi pixeli ici-colo). Ambele valori implicite sunt 10; mărirea lor la sute pentru o țintă din prim-plan de câțiva centimetri elimină fiecare fărâmă de zgomot mic.
x_stride și y_stride setează pasul în pixeli pe care îl face scanerul în timp ce caută un blob de la care să înceapă trasarea. Pasul nu este rezoluția de trasare – trasarea urmează întotdeauna granița reală a blob-ului la nivel de pixel – ci controlează cât de repede găsește scanarea un pixel de pornire. Când se știe că blob-urile sunt mari (o țintă colorată cât un pumn, la o jumătate de metru de cameră, ușor de o sută de pixeli lățime), x_stride=4, y_stride=4 reduce timpul de scanare de șaisprezece ori, fără pierderi practice de detectare. Când blob-urile sunt mici (o baliză LED îndepărtată, de câțiva pixeli lățime), pașii trebuie să rămână la 1 pentru a evita să fie săriți complet. invert inversează testul de prag: potrivirea devine ne-potrivire, iar rutina returnează blob-uri de pixeli care nu trec testul.
threshold_cb este o funcție de retroapelare (callback) Python invocată pe fiecare blob după aplicarea pragului, dar înainte de construirea listei finale de rezultate. Funcția de retroapelare primește blob-ul și returnează True pentru a-l păstra sau False pentru a-l elimina. Acesta este locul în care se aplică filtre Python arbitrare pe proprietăți pe care argumentele cuvânt-cheie nu le expun direct – o densitate minimă, un interval de rotație specific, o combinație personalizată de biți de cod după fuziune. Argumentele cuvânt-cheie sunt filtre în cod nativ și rulează rapid; funcția de retroapelare rulează în Python și este mai lentă, dar nelimitată în ceea ce poate exprima.
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 de1.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 este1.0; blob-urile zimțate sau crestate sunt mai mici.image.get_major_axis_line()șiimage.get_minor_axis_line()returnează obiecteLinede-a lungul axelor majore și minore ale blob-ului, derivate din dreptunghiul rotit de arie minimă.image.get_enclosing_circle()returnează unCirclecare î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ătredraw_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)