5.25. Blobien etsiminen¶
Kynnystäminen muunsi kaapatun kehyksen binääriseksi maskiksi: jokainen pikseli joko läpäisee kynnysarvotestin tai ei läpäise. Tämä vastaa kysymykseen mitkä sovelluksen kannalta kiinnostavat värit esiintyvät kuvassa, mutta ei kysymykseen missä – maski on pelkkä ykkösten ja nollien meri. Seuraava vaihe on blobien tunnistus: maskin läpikäynti, läpäisevien pikselien yhtenäisten alueiden etsiminen ja kunkin palauttaminen oliona, jolla on sijainti, koko, suuntaus ja muut ominaisuudet, joiden perusteella sovellus voi toimia.
find_blobs() on tämän vaiheen tärkein metodi, ja se on yleisin sisäänkäynti image-moduulin tulosolioiden maailmaan. Värillisen pallon seuraaminen, lattiaan maalatun viivan seuraaminen, lämpösensorin näkemien kirkkaiden pisteiden laskeminen, sen päätteleminen onko sininen LED päällä vai pois – sama kutsu kattaa kaikki nämä. Syötteet vaihtuvat (kynnysarvot, haettu alue, tulokseen sovelletut suodattimet), mutta kutsun rakenne on sama.
5.25.1. Peruskutsu¶
find_blobs ottaa listan kynnysarvoja ja palauttaa listan blob-tulosolioita:
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))
Jokaisella kynnysarvotuplella on sama muoto kuin binary()-metodille välitettävillä kynnysarvoilla – kuusi alkiota (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) RGB565-kuvalle (rajat ovat LAB-avaruudessa), kaksi alkiota (lo, hi) harmaasävykuvalle. Yhdessä kutsussa voidaan antaa enintään 32 kynnysarvoa, mikä tekee find_blobs()-metodista niin joustavan: punaisia, vihreitä ja sinisiä majakoita voidaan seurata samanaikaisesti, kukin tuottaa omat blobinsa palautettavaan listaan, ja kunkin blobin code-ominaisuus kertoo, mihin kynnysarvoon se vastasi.
Yllä olevat draw_rectangle()- ja draw_cross()-kutsut merkitsevät kaapatun kehyksen IDE-esikatselua varten. Blob-tuloksessa on jo valmiina b.rect (rajauslaatikko 4-tuplena) sekä b.cx / b.cy (kokonaisluku-keskipiste), joten tunnistuksen piirtäminen takaisin kehykseen vie kaksi metodikutsua.
5.25.2. Mitä tulos sisältää¶
Jokainen Blob on attribuuttitupla, joka pakkaa yhteen kaiken, mitä tunnistin mittasi alueesta. Ominaisuudet jakautuvat neljään ryhmään.
Rajauslaatikko- ja keskipiste-ryhmä – x, y, w, h, rect, cx, cy, cxf, cyf – kuvaa blobin sijaintia. rect on (x, y, w, h)-4-tupla, jota piirtometodit odottavat; cx ja cy ovat keskipiste kokonaislukupikselikoordinaatteina; cxf ja cyf ovat keskipiste alipikselin liukulukukoordinaatteina, mikä on hyödyllistä kun edeltävä kalibrointi tarvitsee murto-osasijainteja.
Muodon kuvaajat – pixels, area, density, perimeter, roundness, elongation, compactness, rotation – kuvaavat blobin ulkonäköä. pixels on läpäisevien pikselien lukumäärä; area on akselien suuntaisen rajauslaatikon pinta-ala (w * h); density on näiden kahden suhde, joka lähestyy arvoa 1.0 täydellä suorakulmiolla ja laskee kohti arvoa 0.0 ohuella vinottaisella viivalla. roundness ja compactness molemmat pisteyttävät, kuinka pyöreä blob on, eri geometrisista näkökulmista (roundness toisen kertaluvun momenteista, compactness piirin ja pinta-alan suhteesta); elongation on mukavuussyistä 1.0 - roundness. rotation on pääakselin suuntaus radiaaneina, joka on tarkin pitkulaisilla blobeilla ja muuttuu kohinaiseksi lähes pyöreillä (epäselvällä akselilla ei ole hyvin määriteltyä suuntaa).
Kynnysarvo- ja yhdistämismetadata – code, count – kertovat, mikä kynnysarvo vastasi ja kuinka monta lähde-blobia yhdistettiin palautettuun blobiin. code on 32-bittinen bittikartta, jossa yksi bitti on asetettu kutakin vastannutta kynnysarvoa kohti (yksittäinen kynnysarvo antaa code == 1; yhdistetyllä monivärisellä blobilla voi olla useita bittejä asetettuna); count on 1, paitsi jos merge=True yhdisti useita tunnistuksia yhdeksi.
Kulmat-ryhmä – corners, min_corners – antaa blobin kierretyn geometrian. corners on 4-tupla (x, y)-ääriarvoja, jotka on poimittu blobin ääriviivasta ja järjestetty myötäpäivään vasemmasta yläkulmasta; min_corners on 4-tupla kulmia pienimmän pinta-alan kierretylle suorakulmiolle, joka ympäröi blobin. Pienimmän pinta-alan suorakulmio on tiukka sovitus; akselien suuntainen rect on löysä, pikseliruudukkoon kohdistettu sovitus. Molemmat ovat hyödyllisiä riippuen siitä, tarvitseeko myöhempi vaihe suunnatun vai tavallisen laatikon.
Blob kantaa mukanaan akselien suuntaisen rajauslaatikon (rect, x, y, w, h), keskipisteen (cx, cy tai alipikselin cxf, cyf), pienimmän pinta-alan kierretyn suorakulmion (min_corners sekä rotation) ja valinnaiset pää- / sivuakselin viivat, jotka alla olevat moduulitason apufunktiot laskevat.¶
5.25.3. Haun suodattaminen¶
Kaapattu kehys sisältää tyypillisesti pikseleitä, jotka vastaavat kynnysarvoa muista syistä kuin sovelluksen kannalta kiinnostavan kohteen vuoksi: peiliheijastukset, kaukaiset taustakohteet, kuvakohinapikselit jotka sattuvat osumaan LAB-alueelle. find_blobs()-metodin avainsana-argumentit ovat ensimmäinen puolustuslinja.
roi rajaa haun tietylle kehyksen alueelle, samalla tavalla kuin kaikki muutkin image-moduulin metodit. Sovellus, joka tietää että kohde voi näkyä vain näkökentän alapuoliskossa, välittää roi=(0, h//2, w, h//2) ja jättää huomiotta kaiken sen yläpuolella; säästetty aika menee takaisin kehysnopeuteen.
area_threshold ja pixels_threshold molemmat suodattavat pois blobit, jotka ovat liian pieniä huomioitaviksi. area_threshold pudottaa blobit, joiden rajauslaatikon pinta-ala on tätä pienempi (hyvä hajanaisen kohinan suodattamiseen); pixels_threshold pudottaa blobit, joilla on tätä vähemmän läpäiseviä pikseleitä (hyvä suurten mutta harvojen blobien suodattamiseen, kuten kynnystetty pisterasterointikuvio, jossa siellä täällä vastaa pari pikseliä). Molempien oletusarvo on 10; niiden nostaminen satoihin etualan kohteelle, joka on muutaman senttimetrin kokoinen, heittää pois jokaisen pienen kohinatäplän.
x_stride ja y_stride asettavat pikseliaskeleen, jonka skanneri ottaa etsiessään blobia, jonka jäljittäminen aloitetaan. Askel ei ole jäljityksen tarkkuus – jäljitys seuraa aina blobin todellista rajaa yksittäisen pikselin tarkkuudella – mutta se ohjaa, kuinka nopeasti skannaus löytää aloituspikselin. Kun blobien tiedetään olevan suuria (nyrkin kokoinen värillinen kohde jalan päässä kamerasta, helposti sadan pikselin levyinen), x_stride=4, y_stride=4 leikkaa skannausajan kuudestoistaosaan ilman käytännön menetystä tunnistuksessa. Kun blobit ovat pieniä (kaukainen LED-majakka, muutaman pikselin levyinen), askelten on pysyttävä arvossa 1, jotta niiden yli ei astuta kokonaan. invert kääntää kynnysarvotestin: vastaavuudesta tulee ei-vastaavuus ja rutiini palauttaa epäonnistuvien pikselien blobit.
threshold_cb on Python-takaisinkutsu, joka kutsutaan jokaiselle blobille kynnystämisen jälkeen mutta ennen lopullisen tuloslistan rakentamista. Takaisinkutsu saa blobin ja palauttaa True säilyttääkseen sen tai False pudottaakseen sen. Tämä on paikka, jossa voi soveltaa mielivaltaisia Python-tason suodattimia ominaisuuksiin, joita avainsana-argumentit eivät suoraan paljasta – vähimmäistiheys, tietty kiertoalue, mukautettu code-bittien yhdistelmä yhdistämisen jälkeen. Avainsana-argumentit ovat natiivikoodin suodattimia ja toimivat nopeasti; takaisinkutsu suoritetaan Pythonissa ja on hitaampi mutta rajaton siinä, mitä se voi ilmaista.
5.25.4. Päällekkäisten blobien yhdistäminen¶
merge=True jälkikäsittelee tuloslistan yhdistääkseen blobit, joiden rajaussuorakulmiot menevät päällekkäin. Luonnollinen käyttö on sellaisen kohteen tunnistaminen, jonka värin kamera näkee useina kynnystettyinä alueina peiliheijastusten, varjoviivojen tai kohteen yli vaihtelevan valaistuksen vuoksi: yksittäinen punainen pallo saattaa palautua kolmena tai neljänä pienenä punaisena blobina, jotka yhdessä jäljittävät pallon. merge=True-asetuksella kolmesta blobista tulee yksi suuri blob, rect kattaa yhdisteen, code on yhdistettyjen blobien koodien bittikohtainen TAI (joten monivärinen yhdistäminen kertoo, mitkä värit vaikuttivat), ja count ilmoittaa, kuinka monta lähde-blobia yhdistettiin.
margin kasvattaa tai pienentää rajaussuorakulmioita ennen päällekkäisyystestiä. margin=2-asetuksella blobit, joiden rajaussuorakulmiot tulevat 2 pikselin etäisyydelle toisistaan, yhdistyvät silti; margin=-2-asetuksella vain blobit, joiden rajaussuorakulmiot menevät päällekkäin vähintään 2 pikselillä, yhdistyvät. Luonnollinen viritys: positiivinen marginaali käsittelemään blobeja, jotka kynnysarvo hajotti vierekkäisiksi paloiksi; negatiivinen marginaali pitämään tiiviisti ryhmittyneet erilliset kohteet erillään.
merge_cb suoritetaan jokaiselle ehdokasparille ennen kuin yhdistäminen tapahtuu. Takaisinkutsu saa kaksi blobia ja palauttaa True salliakseen yhdistämisen tai False estääkseen sen. Tämä on oikea työkalu sellaisten yhdistämisten ristiintarkistukseen, jotka geometrinen sääntö ohittaa – esimerkiksi kieltäytyä yhdistämästä kahta blobia, joiden rotation-kulmat eroavat toisistaan enemmän kuin kynnysarvon verran, tai kieltäytyä yhdistämästä pientä blobia paljon suurempaan, jos pieni on vain täplää.
5.25.5. Projektiohistogrammit¶
x_hist_bins_max ja y_hist_bins_max liittävät kuhunkin blobiin valinnaiset projektiohistogrammit. Projektiohistogrammi on läpäisevien pikselien lukumäärä yhden akselin suuntaisesti: X-akselin histogrammi laskee yhteen läpäisevät pikselit saraketta kohti blobin rajauslaatikon sisällä, ja Y-akselin histogrammi laskee yhteen riviä kohti. Molempien oletusarvo on nolla – histogrammeja ei lasketa, ellei nollasta poikkeavaa max-arvoa anneta, koska ne muuten lisäisivät työtä jokaiseen tunnistukseen.
Kun ne lasketaan, histogrammit tarjoavat edullisen 1-ulotteisen signaalin, jolle sovellus voi suorittaa lisäanalyysiä: blobin sisällä olevan pystyraidan sijainnin tunnistaminen, kaksivärisen kohteen taitekohdan löytäminen, pitkän akselin suuntaisten aukkojen lukumäärän laskeminen. Ne täytetään kunkin Blob-olion x_hist_bins- ja y_hist_bins-ominaisuuksiin.
5.25.6. Ylimääräiset geometriset apufunktiot¶
Kourallinen muita geometrisia mittauksia on toteutettu moduulitason funktioina, jotka ottavat blobin ja palauttavat pyydetyn mittauksen:
image.get_solidity()palauttaa blobin kiinteyden – pikselit jaettuna konveksin peitteen pinta-alalla. Kiinteä täytetty alue on lähellä arvoa1.0; blob, jossa on koveruuksia (hevosenkenkä, käsi sormet harallaan), laskee selvästi sen alle.image.get_convexity()palauttaa konveksisuuden – konveksin peitteen piiri jaettuna blobin piirillä. Täydellisen konveksin blobin arvo on1.0; rosoisten tai loveltujen blobien arvo on matalampi.image.get_major_axis_line()jaimage.get_minor_axis_line()palauttavatLine-olioita blobin pää- ja sivuakselien suuntaisesti, johdettuna kierretystä pienimmän pinta-alan suorakulmiosta.image.get_enclosing_circle()palauttaaCircle-olion, joka ympäröi blobin – hyödyllistä kun myöhempi vaihe haluaa ympyrän piirrettäväksi tai vertailtavaksi.image.get_enclosed_ellipse()palauttaa 5-tuplan(cx, cy, rx, ry, rotation)ellipsille, joka on piirretty blobin pienimmän pinta-alan suorakulmion sisään. Arvot syötetään suoraandraw_ellipse()-metodille.
5.25.7. Kynnysarvon automaattinen oppiminen¶
Blob-tunnistin on vain niin hyvä kuin kynnysarvot, joilla sitä ajetaan, ja kohdevärille oikean kynnysarvon löytäminen on oma ongelmansa. Kaksi yleistä toimintatapaa vähentää tätä työtä.
Ensimmäinen on vuorovaikutteinen valinta IDE:ssä: kaappaa kehys, vedä suorakulmio kohdevärin esimerkin ympärille ja anna IDE:n kynnysarvoeditorin raportoida näkemänsä LAB-rajat. Nämä rajat pudotetaan skriptiin find_blobs()-kynnysarvoiksi ja tunnistin on valmis.
Toinen on ohjelmallinen automaattinen oppiminen: kamerassa ajettava kalibrointirutiini kaappaa kehyksen, ottaa histogrammin tunnetusta laikusta jossa kohde on (get_histogram() argumentilla roi=), ja lukee laikun arvoalueen histogrammista get_percentile()-metodilla. 5. prosenttipiste asettaa kunkin kanavan alarajan ja 95. sen ylärajan, jättäen huomiotta yksittäiset poikkeavat pikselit molemmissa päissä. RGB565-kuvalla yksi prosenttipistekutsu raportoi kaikki kolme LAB-kanavaa kerralla, joten kaksi kutsua tuottaa ne kuusi lukua, joita find_blobs() odottaa:
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)