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 kuvaajatpixels, 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ämismetadatacode, 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.

Blobin tunnistus havainnollistettuna binääristä kynnysarvomaskia vasten. Vasen paneeli näyttää kallistetun soikean maskin läpäisevistä pikseleistä. Oikea paneeli näyttää saman maskin merkittynä sen ympärille piirretyllä akselien suuntaisella rajauslaatikolla, keskipisteen ristillä merkittynä keskellä, katkoviivaisella pienimmän pinta-alan kierretyllä suorakulmiolla, joka myötäilee soikiota sen todellisessa kulmassa, sekä pääakselin viivalla, joka kulkee keskipisteen läpi soikion pitkän suunnan mukaisesti.

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.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ä arvoa 1.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 on 1.0; rosoisten tai loveltujen blobien arvo on matalampi.

  • image.get_major_axis_line() ja image.get_minor_axis_line() palauttavat Line-olioita blobin pää- ja sivuakselien suuntaisesti, johdettuna kierretystä pienimmän pinta-alan suorakulmiosta.

  • image.get_enclosing_circle() palauttaa Circle-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 suoraan draw_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)