5.25. Hledání blobů¶
Prahování proměnilo zachycený snímek na binární masku: každý pixel buď projde testem prahu, nebo neprojde. To odpovídá na otázku, které barvy, na nichž aplikaci záleží, se ve scéně objevují, ale ne kde – maska je jen moře jedniček a nul. Dalším krokem je detekce blobů: procházení masky, hledání souvislých oblastí procházejících pixelů a vrácení každé z nich jako objektu s pozicí, velikostí, orientací a dalšími vlastnostmi, podle kterých může aplikace jednat.
find_blobs() je tažnou metodou tohoto kroku a je nejběžnějším vstupním bodem do světa výsledkových objektů modulu image. Sledování barevného míče, následování čáry namalované na podlaze, počítání, kolik jasných bodů vidí termální senzor, rozhodování, zda modrá LED svítí nebo ne – totéž volání pokrývá je všechny. Vstupy se mění (prahy, prohledávaná oblast, filtry aplikované na výsledek), ale vzor volání je stejný.
5.25.1. Základní volání¶
find_blobs přijímá seznam prahů a vrací seznam výsledkových objektů 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))
Každá n-tice prahu má stejný tvar jako prahy předávané metodě binary() – šest položek (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) pro obraz RGB565 (meze jsou v LAB), dvě položky (lo, hi) pro obraz ve stupních šedi. V jediném volání lze zadat až 32 prahů, což činí metodu find_blobs() tak flexibilní: červené, zelené a modré majáky lze sledovat současně, každý přispívá svými vlastními bloby do vráceného seznamu a vlastnost code každého blobu identifikuje, kterému prahu odpovídal.
Volání draw_rectangle() a draw_cross() výše označí zachycený snímek pro náhled v IDE. Výsledek blobu již nese b.rect (ohraničující rámeček jako 4-tici) a b.cx / b.cy (celočíselný centroid), takže vykreslení detekce zpět do snímku jsou dvě volání metod.
5.25.2. Co výsledek obsahuje¶
Každý Blob je atributová n-tice, která sdružuje vše, co detektor o oblasti změřil. Vlastnosti se dělí do čtyř skupin.
Skupina ohraničujícího rámečku a centroidu – x, y, w, h, rect, cx, cy, cxf, cyf – popisuje pozici blobu. rect je 4-tice (x, y, w, h), kterou očekávají metody kreslení; cx a cy jsou centroid v celočíselných pixelových souřadnicích; cxf a cyf jsou centroid v subpixelových float souřadnicích, užitečných tam, kde předřazené kalibraci záleží na zlomkových pozicích.
Deskriptory tvaru – pixels, area, density, perimeter, roundness, elongation, compactness, rotation – popisují, jak blob vypadá. pixels je počet procházejících pixelů; area je plocha osově zarovnaného ohraničujícího rámečku (w * h); density je poměr obou, který se blíží 1.0 pro plný obdélník a klesá k 0.0 pro tenký diagonální tah. roundness a compactness obě hodnotí, jak je blob kulatý, z různých geometrických hledisek (roundness z momentů druhého řádu, compactness z poměru obvodu k ploše); elongation je pro pohodlí 1.0 - roundness. rotation je orientace hlavní osy v radiánech, která je nejpřesnější u protáhlých blobů a stává se zašuměnou u téměř kulatých (nejednoznačná osa nemá dobře definovaný směr).
Metadata prahu a sloučení – code, count – identifikují, kterému prahu blob odpovídal a kolik zdrojových blobů bylo sloučeno do vráceného. code je 32bitová bitová mapa s jedním nastaveným bitem na každý odpovídající práh (jeden práh dává code == 1; sloučený vícebarevný blob může mít nastaveno několik bitů); count je 1, pokud merge=True nesloučilo několik detekcí do jedné.
Skupina rohů – corners, min_corners – udává otočenou geometrii blobu. corners je 4-tice extrémů (x, y) vytažených z kontury blobu, seřazených ve směru hodinových ručiček od levého horního rohu; min_corners je 4-tice rohů otočeného obdélníku s minimální plochou, který blob obklopuje. Obdélník s minimální plochou je těsným přizpůsobením; osově zarovnaný rect je volným přizpůsobením zarovnaným s pixelovou mřížkou. Oba jsou užitečné podle toho, zda následující fáze potřebuje orientovaný rámeček, nebo prostý.
Blob nese osově zarovnaný ohraničující rámeček (rect, x, y, w, h), centroid (cx, cy nebo subpixelové cxf, cyf), otočený obdélník s minimální plochou (min_corners plus rotation) a volitelné čáry hlavní / vedlejší osy vypočítané pomocníky na úrovni modulu níže.¶
5.25.3. Filtrování vyhledávání¶
Zachycený snímek obvykle obsahuje pixely, které odpovídají prahu z jiných důvodů než kvůli objektu, na němž aplikaci záleží: zrcadlové odlesky, vzdálené objekty v pozadí, šumové pixely obrazu, které náhodou spadají do rozsahu LAB. Klíčové argumenty metody find_blobs() jsou první linií obrany.
roi omezuje vyhledávání na oblast snímku stejně jako každá jiná metoda modulu image. Aplikace, která ví, že se objekt může objevit pouze v dolní polovině zorného pole, předá roi=(0, h//2, w, h//2) a ignoruje vše nad ní; ušetřený čas se vrací do snímkové frekvence.
area_threshold a pixels_threshold obě filtrují bloby, které jsou příliš malé na to, aby na nich záleželo. area_threshold zahazuje bloby, jejichž ohraničující rámeček má méně než tolik pixelů plochy (vhodné pro filtrování rozptýleného šumu); pixels_threshold zahazuje bloby, které mají méně než tolik procházejících pixelů (vhodné pro filtrování blobů, které jsou velké, ale řídké, jako naprahovaný tečkovaný vzor s jedním nebo dvěma odpovídajícími pixely tu a tam). Obě výchozí hodnoty jsou 10; jejich navýšení na stovky pro popředový cíl o velikosti několika centimetrů odhodí každou skvrnku malého šumu.
x_stride a y_stride nastavují pixelový krok, který skener provádí, když hledá blob, jehož trasování má začít. Krok není rozlišením trasování – trasování vždy sleduje skutečnou hranici blobu s jednopixelovým detailem – ale řídí, jak rychle skenování najde počáteční pixel. Pokud je známo, že bloby jsou velké (barevný cíl velikosti pěsti půl metru od kamery, snadno sto pixelů napříč), x_stride=4, y_stride=4 zkrátí čas skenování šestnáctkrát bez praktické ztráty při detekci. Když jsou bloby malé (vzdálený LED maják, několik pixelů napříč), kroky musí zůstat na 1, aby je nepřekročily úplně. invert obrátí test prahu: odpovídání se stane neodpovídáním a rutina vrátí bloby neprocházejících pixelů místo procházejících.
threshold_cb je Python callback vyvolaný na každém blobu po prahování, ale před sestavením konečného seznamu výsledků. Callback obdrží blob a vrátí True pro jeho ponechání nebo False pro jeho zahození. Toto je místo pro aplikaci libovolných filtrů na úrovni Pythonu na vlastnosti, které klíčové argumenty přímo nezpřístupňují – minimální hustotu, konkrétní rozsah rotace, vlastní kombinaci bitů kódu po sloučení. Klíčové argumenty jsou filtry v nativním kódu a běží rychle; callback běží v Pythonu a je pomalejší, ale neomezený v tom, co dokáže vyjádřit.
5.25.4. Slučování překrývajících se blobů¶
merge=True dodatečně zpracovává seznam výsledků a slučuje bloby, jejichž ohraničující obdélníky se překrývají. Přirozeným použitím je detekce cíle, jehož barvu kamera vidí jako několik naprahovaných oblastí kvůli zrcadlovým odleskům, stínovým liniím nebo nesourodému osvětlení napříč objektem: jeden červený míč se může vrátit jako tři nebo čtyři malé červené bloby, které dohromady vystihují míč. S merge=True se ze tří blobů stane jeden velký blob, rect pokrývá sjednocení, code je bitový OR kódů sloučených blobů (takže vícebarevné sloučení identifikuje, které barvy přispěly) a count udává, kolik zdrojových blobů bylo zkombinováno.
margin zvětšuje nebo zmenšuje ohraničující obdélníky před testem překrytí. S margin=2 se sloučí i bloby, jejichž ohraničující obdélníky se k sobě přiblíží na 2 pixely; s margin=-2 se sloučí pouze bloby, jejichž ohraničující obdélníky se překrývají alespoň o 2 pixely. Přirozené ladění: kladný margin pro zacházení s bloby, které práh rozbil na sousedící kusy; záporný margin pro udržení těsně seskupených odlišných objektů odděleně.
merge_cb se spustí na každém kandidátním páru, než dojde ke sloučení. Callback obdrží dva bloby a vrátí True pro povolení sloučení nebo False pro jeho zabránění. Toto je správný nástroj pro křížovou kontrolu sloučení, která geometrické pravidlo míjí – například odmítnutí sloučení dvou blobů, jejichž úhly rotation se liší o více než práh, nebo odmítnutí sloučení malého blobu do mnohem většího, pokud je malý blob jen skvrnka.
5.25.5. Projekční histogramy¶
x_hist_bins_max a y_hist_bins_max připojují ke každému blobu volitelné projekční histogramy. Projekční histogram je počet procházejících pixelů podél jedné osy: histogram osy X sčítá procházející pixely na sloupec uvnitř ohraničujícího rámečku blobu a histogram osy Y sčítá na řádek. Oba mají výchozí hodnotu nula – histogramy se nepočítají, pokud není zadáno nenulové max, protože by jinak přidávaly práci ke každé detekci.
Když jsou vypočítány, histogramy poskytují levný 1-D signál, na kterém může aplikace provádět další analýzu: detekci pozice svislého pruhu uvnitř blobu, hledání zlomu dvoubarevného cíle, počítání, kolik mezer se objeví podél dlouhé osy. Jsou naplněny jako vlastnosti x_hist_bins a y_hist_bins na každém Blob.
5.25.6. Další geometričtí pomocníci¶
Hrstka dalších geometrických měr existuje jako funkce na úrovni modulu, které přijímají blob a vracejí požadované měření:
image.get_solidity()vrací plnost (solidity) blobu – pixely vydělené plochou konvexního obalu. Plná vyplněná oblast je blízko1.0; blob s konkávnostmi (podkova, ruka s roztaženými prsty) klesá výrazně níže.image.get_convexity()vrací konvexitu – obvod konvexního obalu vydělený obvodem blobu. Dokonale konvexní blob je1.0; zubaté nebo zářezem opatřené bloby jsou nižší.image.get_major_axis_line()aimage.get_minor_axis_line()vracejí objektyLinepodél hlavní a vedlejší osy blobu, odvozené z otočeného obdélníku s minimální plochou.image.get_enclosing_circle()vracíCircle, který blob obklopuje – užitečné, když následující fáze chce kruh ke kreslení nebo k porovnání.image.get_enclosed_ellipse()vrací 5-tici(cx, cy, rx, ry, rotation)pro elipsu vepsanou do obdélníku s minimální plochou blobu. Hodnoty vstupují přímo dodraw_ellipse().
5.25.7. Automatické naučení prahu¶
Detektor blobů je jen tak dobrý jako prahy, se kterými je spuštěn, a práce s nalezením správného prahu pro cílovou barvu je sama o sobě problémem. Tuto práci snižují dva běžné vzory.
Prvním je interaktivní výběr v IDE: zachyťte snímek, přetáhněte obdélník kolem příkladu cílové barvy a nechte editor prahů v IDE nahlásit meze LAB, které vidí. Tyto meze se vloží do skriptu jako prahy find_blobs() a detektor je připraven.
Druhým je programové automatické naučení: kalibrační rutina běžící na kameře zachytí snímek, vezme histogram známé plošky, kde se cíl nachází (get_histogram() s roi=), a odečte rozsah hodnot plošky z histogramu pomocí get_percentile(). 5. percentil nastaví dolní mez každého kanálu a 95. jeho horní mez, přičemž ignoruje zatoulané odlehlé pixely na obou koncích. Na obrazu RGB565 jedno volání percentilu nahlásí všechny tři kanály LAB najednou, takže dvě volání produkují šest čísel, která find_blobs() očekává:
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)