5.25. Blobs vinden¶
Drempelwaardebepaling zette het vastgelegde frame om in een binair masker: elke pixel slaagt voor de drempelwaardetest of niet. Dat beantwoordt welke kleuren waar de applicatie om geeft in de scène voorkomen, maar niet waar – het masker is slechts een zee van enen en nullen. De volgende stap is blob-detectie: het masker doorlopen, aaneengesloten gebieden van geslaagde pixels vinden, en elk daarvan teruggeven als een object met een positie, een grootte, een oriëntatie en de andere eigenschappen waarop een applicatie kan reageren.
find_blobs() is de werkpaardmethode voor die stap, en het is het meest gebruikte toegangspunt tot de wereld van resultaatobjecten van de image-module. Een gekleurde bal volgen, een op de vloer geschilderde lijn volgen, tellen hoeveel heldere plekken een thermische sensor ziet, beslissen of een blauwe LED aan of uit staat – dezelfde aanroep dekt al deze gevallen. De invoer verandert (de drempelwaarden, het doorzochte gebied, de filters die op het resultaat worden toegepast), maar het aanroeppatroon is hetzelfde.
5.25.1. De basisaanroep¶
find_blobs neemt een lijst met drempelwaarden en geeft een lijst met blob-resultaatobjecten terug:
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))
Elke drempelwaarde-tuple heeft dezelfde vorm als de drempelwaarden die aan binary() worden doorgegeven – zes elementen (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) voor een RGB565-afbeelding (de grenzen zijn in LAB), twee elementen (lo, hi) voor een grijswaardenafbeelding. Tot 32 drempelwaarden kunnen in één enkele aanroep worden opgegeven, wat find_blobs() zo flexibel maakt: rode, groene en blauwe bakens kunnen gelijktijdig worden gevolgd, waarbij elk zijn eigen blobs bijdraagt aan de teruggegeven lijst, en de code-eigenschap van elke blob identificeert met welke drempelwaarde deze overeenkwam.
De bovenstaande aanroepen draw_rectangle() en draw_cross() annoteren het vastgelegde frame voor de IDE-voorbeeldweergave. Het blob-resultaat bevat al b.rect (het begrenzingsvak als 4-tuple) en b.cx / b.cy (het gehele zwaartepunt), dus de detectie terugtekenen in het frame is een kwestie van twee methode-aanroepen.
5.25.2. Wat het resultaat bevat¶
Elke Blob is een attribuut-tuple die alles bundelt wat de detector over het gebied heeft gemeten. De eigenschappen vallen in vier groepen uiteen.
De groep begrenzingsvak en zwaartepunt – x, y, w, h, rect, cx, cy, cxf, cyf – beschrijft de positie van de blob. rect is de (x, y, w, h) 4-tuple die tekenmethoden verwachten; cx en cy zijn het zwaartepunt in gehele pixelcoördinaten; cxf en cyf zijn het zwaartepunt in subpixel-floatcoördinaten, nuttig wanneer een eerdere kalibratie om fractionele posities geeft.
De vormdescriptoren – pixels, area, density, perimeter, roundness, elongation, compactness, rotation – beschrijven hoe de blob eruitziet. pixels is het aantal geslaagde pixels; area is de oppervlakte van het asgerichte begrenzingsvak (w * h); density is de verhouding tussen die twee, die 1.0 benadert voor een massieve rechthoek en daalt richting 0.0 voor een dunne diagonale streep. roundness en compactness scoren beide hoe rond de blob is, vanuit verschillende geometrische gezichtspunten (roundness vanuit de tweede-orde momenten, compactness vanuit de verhouding tussen omtrek en oppervlakte); elongation is voor het gemak 1.0 - roundness. rotation is de oriëntatie van de hoofdas in radialen, die het nauwkeurigst is bij langgerekte blobs en ruizig wordt bij vrijwel ronde (een dubbelzinnige as heeft geen goed gedefinieerde richting).
De drempelwaarde- en samenvoeg-metadata – code, count – identificeren welke drempelwaarde overeenkwam en hoeveel bronblobs er werden samengevoegd tot de teruggegeven blob. code is een 32-bits bitmap met één bit gezet per overeenkomende drempelwaarde (een enkele drempelwaarde geeft code == 1; een samengevoegde meerkleurige blob kan meerdere bits gezet hebben); count is 1 tenzij merge=True meerdere detecties tot één combineerde.
De groep hoeken – corners, min_corners – geeft de geroteerde geometrie van de blob. corners is de 4-tuple van (x, y)-uitersten die uit de contour van de blob is gehaald, met de klok mee gesorteerd vanaf linksboven; min_corners is de 4-tuple van hoeken voor de geroteerde rechthoek met minimale oppervlakte die de blob omsluit. De rechthoek met minimale oppervlakte is de strakke pasvorm; de asgerichte rect is de losse pasvorm uitgelijnd met het pixelraster. Beide zijn nuttig, afhankelijk van of een volgende fase een georiënteerd vak of een gewoon vak nodig heeft.
Een blob draagt het asgerichte begrenzingsvak (rect, x, y, w, h), het zwaartepunt (cx, cy of subpixel cxf, cyf), de geroteerde rechthoek met minimale oppervlakte (min_corners plus rotation), en de optionele hoofd- / nevenas-lijnen die worden berekend door de onderstaande hulpfuncties op moduleniveau.¶
5.25.3. De zoekopdracht filteren¶
Een vastgelegd frame bevat doorgaans pixels die overeenkomen met de drempelwaarde om andere redenen dan het object waar de applicatie om geeft: spiegelende lichtvlekken, verre achtergrondobjecten, beeldruispixels die toevallig binnen het LAB-bereik vallen. De trefwoordargumenten voor find_blobs() zijn de eerste verdedigingslinie.
roi beperkt de zoekopdracht tot een gebied van het frame, op dezelfde manier als elke andere methode van de image-module. Een applicatie die weet dat het object alleen in de onderste helft van het gezichtsveld kan verschijnen geeft roi=(0, h//2, w, h//2) door en negeert alles erboven; de bespaarde tijd komt ten goede aan de framesnelheid.
area_threshold en pixels_threshold filteren beide blobs die te klein zijn om je druk over te maken. area_threshold verwijdert blobs waarvan het begrenzingsvak minder dan dat aantal pixels oppervlakte heeft (goed voor het filteren van verspreide ruis); pixels_threshold verwijdert blobs die minder dan dat aantal geslaagde pixels hebben (goed voor het filteren van blobs die groot maar schaars zijn, zoals een gedrempeld stippelpatroon waarbij hier en daar een of twee pixels overeenkomen). Beide standaardwaarden zijn 10; ze opvoeren naar honderden voor een voorgrondobject van enkele centimeters breed gooit elk klein ruisspikkeltje weg.
x_stride en y_stride stellen de pixelstap in die de scanner neemt terwijl hij zoekt naar een blob om te beginnen met traceren. Stride is niet de traceerresolutie – de tracering volgt altijd de werkelijke blobgrens met detail op pixelniveau – maar het bepaalt hoe snel de scan een startpixel vindt. Wanneer bekend is dat blobs groot zijn (een vuistgroot gekleurd doelwit op een halve meter van de cam, gemakkelijk honderd pixels breed), verkort x_stride=4, y_stride=4 de scantijd met een factor zestien zonder praktisch verlies in detectie. Wanneer blobs klein zijn (een ver verwijderd LED-baken, enkele pixels breed), moeten de strides op 1 blijven om te voorkomen dat er volledig overheen wordt gestapt. invert keert de drempelwaardetest om: overeenkomen wordt niet-overeenkomen en de routine geeft blobs van niet-geslaagde pixels terug.
threshold_cb is een Python-callback die op elke blob wordt aangeroepen na de drempelwaardebepaling maar voordat de uiteindelijke resultaatlijst wordt opgebouwd. De callback ontvangt de blob en geeft True terug om deze te behouden of False om deze te verwijderen. Dit is de plek om willekeurige filters op Python-niveau toe te passen op eigenschappen die de trefwoordargumenten niet rechtstreeks blootleggen – een minimale dichtheid, een specifiek rotatiebereik, een aangepaste code-bitcombinatie na samenvoegen. De trefwoordargumenten zijn filters in native code en draaien snel; de callback draait in Python en is langzamer maar onbeperkt in wat hij kan uitdrukken.
5.25.4. Overlappende blobs samenvoegen¶
merge=True verwerkt de resultaatlijst achteraf om blobs te combineren waarvan de begrenzingsrechthoeken overlappen. Het natuurlijke gebruik is het detecteren van een doelwit waarvan de camera de kleur als meerdere gedrempelde gebieden ziet vanwege spiegelende lichtvlekken, schaduwlijnen of ongelijkmatige belichting over het object: een enkele rode bal kan terugkomen als drie of vier kleine rode blobs die samen de bal traceren. Met merge=True worden de drie blobs één grote blob, dekt de rect de vereniging, is de code de bitsgewijze OR van de codes van de samengevoegde blobs (dus een meerkleurige samenvoeging identificeert welke kleuren hebben bijgedragen), en rapporteert count hoeveel bronblobs er werden gecombineerd.
margin vergroot of verkleint de begrenzingsrechthoeken vóór de overlaptest. Met margin=2 worden blobs waarvan de begrenzingsrechthoeken binnen 2 pixels van elkaar komen nog steeds samengevoegd; met margin=-2 worden alleen blobs samengevoegd waarvan de begrenzingsrechthoeken met ten minste 2 pixels overlappen. De natuurlijke afstemming: een positieve marge om blobs te verwerken die de drempelwaarde in aangrenzende stukken heeft opgebroken; een negatieve marge om dicht op elkaar gegroepeerde, afzonderlijke objecten gescheiden te houden.
merge_cb draait op elk kandidaatpaar voordat de samenvoeging plaatsvindt. De callback ontvangt de twee blobs en geeft True terug om de samenvoeging toe te staan of False om deze te voorkomen. Dit is het juiste hulpmiddel om samenvoegingen te controleren die de geometrische regel mist – bijvoorbeeld weigeren om twee blobs samen te voegen waarvan de rotation-hoeken meer dan een drempelwaarde verschillen, of weigeren om een kleine blob samen te voegen met een veel grotere als de kleine slechts spikkels is.
5.25.5. Projectiehistogrammen¶
x_hist_bins_max en y_hist_bins_max koppelen optionele projectiehistogrammen aan elke blob. Een projectiehistogram is het aantal geslaagde pixels langs één as: het X-as-histogram telt de geslaagde pixels per kolom binnen het begrenzingsvak van de blob op, en het Y-as-histogram telt per rij op. Beide hebben standaard de waarde nul – de histogrammen worden niet berekend tenzij een max die niet nul is wordt opgegeven, aangezien ze anders aan elke detectie werk zouden toevoegen.
Wanneer ze worden berekend, leveren de histogrammen een goedkoop 1D-signaal waarop een applicatie verdere analyse kan uitvoeren: het detecteren van de positie van een verticale streep binnen de blob, het vinden van het breekpunt van een tweekleurig doelwit, het tellen van hoeveel gaten er langs de lange as verschijnen. Ze worden gevuld als de eigenschappen x_hist_bins en y_hist_bins op elke Blob.
5.25.6. Extra geometrische hulpfuncties¶
Een handvol verdere geometrische maatstaven bestaat als functies op moduleniveau die een blob nemen en de gevraagde meting teruggeven:
image.get_solidity()geeft de soliditeit van de blob terug – pixels gedeeld door de oppervlakte van de convexe omhulling. Een massief gevuld gebied ligt dicht bij1.0; een blob met holtes (een hoefijzer, een hand met gespreide vingers) zakt daar ruim onder.image.get_convexity()geeft de convexiteit terug – de omtrek van de convexe omhulling gedeeld door de omtrek van de blob. Een perfect convexe blob is1.0; gekartelde of ingekeepte blobs zijn lager.image.get_major_axis_line()enimage.get_minor_axis_line()gevenLine-objecten terug langs de hoofd- en nevenassen van de blob, afgeleid van de geroteerde rechthoek met minimale oppervlakte.image.get_enclosing_circle()geeft eenCircleterug die de blob omsluit – nuttig wanneer een volgende fase een cirkel wil tekenen of ertegen wil testen.image.get_enclosed_ellipse()geeft de 5-tuple(cx, cy, rx, ry, rotation)terug voor een ellips die is ingeschreven in de rechthoek met minimale oppervlakte van de blob. De waarden voeden rechtstreeks indraw_ellipse().
5.25.7. Een drempelwaarde automatisch aanleren¶
Een blob-detector is slechts zo goed als de drempelwaarden waarmee hij wordt uitgevoerd, en het werk van het vinden van de juiste drempelwaarde voor een doelkleur is een probleem op zich. Twee veelvoorkomende patronen verminderen dat werk.
Het eerste is interactieve selectie in de IDE: leg een frame vast, sleep een rechthoek rond een voorbeeld van de doelkleur, en laat de drempelwaarde-editor van de IDE de LAB-grenzen rapporteren die hij ziet. Die grenzen vallen in het script als de drempelwaarden van find_blobs() en de detector is klaar.
Het tweede is programmatisch automatisch aanleren: een kalibratieroutine die op de camera draait legt een frame vast, neemt een histogram van een bekende vlek waar het doelwit zich bevindt (get_histogram() met roi=), en leest het waardebereik van de vlek af van het histogram met get_percentile(). Het 5e percentiel stelt de ondergrens van elk kanaal in en het 95e de bovengrens, waarbij verdwaalde uitschieterpixels aan beide uiteinden worden genegeerd. Op een RGB565-afbeelding rapporteert één percentielaanroep alle drie de LAB-kanalen tegelijk, dus de twee aanroepen produceren de zes getallen die find_blobs() verwacht:
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)