5.26. Viivojen ja segmenttien etsiminen¶
Jotkin näkymän piirteet eivät ole yhtenäisiä värialueita vaan suuntautuneita suoria reunoja: lattiaan maalattu viiva, kahden pinnan välinen sauma, painetun suorakulmion sivu, oviaukon reuna. Blob-tunnistimen pyytäminen löytämään ne on väärä kysymys – reuna on yhden pikselin levyinen, blob-algoritmi haluaa pinta-alaa-ja-väriä, ja vastaus tulee takaisin tyhjänä tai kohisevana.
Oikea tunnistin suuntautuneille reunoille on Hough-viivamuunnos. image-moduuli tarjoaa sen kahdessa muodossa: find_lines() palauttaa äärettömiä viivoja (jokainen viiva ulottuu koko kuvan poikki); find_line_segments() palauttaa äärellisiä segmenttejä (jokaisella viivalla on päätepisteet kehyksen sisällä). Kumpaa sovellus tarvitsee, riippuu siitä, ovatko kiinnostavat reunat jatkuvia koko kehyksen poikki vai kattavatko ne vain osan siitä.
5.26.1. Kuinka Hough-muunnos toimii¶
Molemmat tunnistimet jakavat saman ydinajatuksen, joten se kannattaa ymmärtää kerran. image-moduuli ajaa ensin Sobel-tyyppisen reunasuodattimen syötteelle pisteyttääkseen jokaisen pikselin sen mukaan, kuinka todennäköisesti se sijaitsee suuntautuneella reunalla. Jokainen tällainen reunapikseli sitten äänestää kaikkia viivoja, joilla se saattaa sijaita. Eniten ääniä keränneet viivat voittavat.
Viiva parametrisoidaan Hough-avaruudessa kahdella luvulla: theta, viivan kulma (0 – 179 astetta), ja rho, kohtisuora etäisyys kuvan origosta viivaan (etumerkillinen, pikseleinä). Jokainen kuvan sisältämä viiva on yksi piste (theta, rho)-avaruudessa. Jokainen syötteen reunapikseli antaa yhden äänen jokaiselle sijaintinsa kanssa yhteensopivalle (theta, rho)-yhdistelmälle – käsitteellisesti käyrän Hough-avaruuden läpi. Siellä, missä monet tällaiset käyrät risteävät, monet reunapikselit ovat yhtä mieltä samasta viivasta, ja tuo risteys on tunnistus.
Tunnistin palauttaa Hough-avaruuden paikalliset maksimit, joiden äänimäärät ylittävät kynnysarvon. Jokainen palautettu Line kantaa molemmat esitysmuodot: x1, y1, x2, y2 päätepistemuodolle (leikattuna kuvan rajoihin äärettömässä tapauksessa), theta, rho Hough-muodolle sekä length ja magnitude koolle ja äänimäärälle vastaavasti.
5.26.2. Äärettömät viivat¶
find_lines() ajaa Hough-muunnoksen ja palauttaa vahvimmat viivat, kukin ulotettuna koko kuvan poikki:
lines = img.find_lines(threshold=1500, theta_margin=25, rho_margin=25)
for l in lines:
img.draw_line(l, color=(255, 0, 0))
threshold on viivan hyväksymiseen vaadittava pienin äänimäärä. Äänimäärä laskee yhteen jokaisen myötävaikuttavan pikselin Sobel-reunasuuruudet, joten suuremmat threshold-arvot vaativat pidempiä tai vahvempia reunoja läpäistäkseen – mikä tekee oikeasta arvosta riippuvaisen kuvan resoluutiosta (pidempi viiva korkeammalla resoluutiolla kerää enemmän ääniä) sekä näkymästä, joten se on viritettävä kyseiselle sovellukselle. Karkeina lähtökohtina viritykseen: 1000 vaatimattomalle viivalle selkeässä kuvassa, 500 tai alle heikolle kontrastille tai lyhyille viivoille, 2000 tai enemmän vilkkaille näkymille, joissa väärät viivat muodostuvat reunakohinan ryppäiden kautta.
theta_margin ja rho_margin säätävät lähekkäisten maksimien yhdistämistä. Yksittäinen fyysinen reuna tuottaa pienen ryppään runsasäänisiä lokeroita todellisen (theta, rho):n ympärille, ja tunnistin tiivistää kunkin ryppään huippuunsa ennen palauttamista. theta_margin=25 (astetta) yhdistää kaikki huiput 25 asteen suuntauseron sisällä; rho_margin=25 (pikseliä) yhdistää huiput 25 pikselin etäisyyden sisällä. Oletusarvot ovat kohtuullisia; niiden nostaminen palauttaa vähemmän, selvempiä viivoja ja laskeminen palauttaa enemmän, joskus kaksoiskappaleita.
x_stride ja y_stride askeltavat reunapikselien läpi äänestyksen aikana, samaan tapaan kuin ne askeltavat pikselien läpi metodissa find_blobs(). Oletukset 2 ja 1 toimivat yleisessä tapauksessa; niiden nostaminen nopeuttaa hakua resoluution kustannuksella. roi rajaa haun kehyksen alueeseen, mikä sekä kaventaa palautettuja viivoja että vähentää työtä.
Jokainen palautettu viiva on piirrettävissä suoraan: Line-olio menee suoraan metodiin draw_line(), joka lukee (x1, y1, x2, y2)-päätepistekentät sen alusta. l.theta on kulma asteina, mikä luokittelee viivan vaaka-, pysty- tai vinoviivaksi yhdellä vertailulla. l.magnitude on äänimäärä, joka lajittelee palautetut viivat vahvimmasta heikoimpaan.
5.26.3. Viivasegmentit¶
find_lines() on oikea tunnistin reunoille, jotka ulottuvat koko kehyksen poikki, mutta monet todelliset reunat – painetun viivakoodin vasen sivu, luokkanimen yläreuna, viivaimen näkyvä sivu – kulkevat vain osan kuvan poikki. find_line_segments() palauttaa äärellisiä segmenttejä, joiden päätepisteet ovat kehyksen sisällä:
segments = img.find_line_segments(merge_distance=5, max_theta_difference=10)
for s in segments:
img.draw_line(s, color=(0, 255, 0))
Segmenttitunnistin jäljittää suuntautuneita reunapikseleitä pitkin suoraan sen sijaan, että äänestäisi Hough-avaruudessa, ja tuloksena on kokoelma lyhyitä suoria pätkiä. merge_distance asettaa suurimman pikseliraon, jonka kaksi samansuuntaista lyhyttä pätkää voivat ylittää ja silti yhdistyä yhdeksi palautetuksi segmentiksi; max_theta_difference asettaa, kuinka monta astetta suuntauseroa yhdistäjä sietää vierekkäisten pätkien välillä. Antelias yhdistäminen (merge_distance=10, max_theta_difference=15) palauttaa pienen määrän pitkiä segmenttejä sen kustannuksella, että se joskus siltaa aidosti erillisiä reunoja; tiukka yhdistäminen (merge_distance=0, max_theta_difference=5) palauttaa monia lyhyitä segmenttejä ja antaa sovelluksen lajitella ne Pythonissa.
Tulosoliot ovat samaa Line-tyyppiä kuin find_lines() palauttaa, samoine ominaisuuksineen, joten putki voi käsitellä kummankin tyyppistä tunnistusta saman alavirran koodipolun läpi. Ainoa käytännön ero on, että segmenttien päätepisteet ovat viivan todelliset päät kuvassa, kun taas äärettömien viivojen päätepisteet ovat siellä, missä viiva ylittää kuvan reunan.
5.26.4. Milloin kumpaakin käytetään¶
Valinta kahden metodin välillä kiteytyy yhteen kysymykseen: välittääkö sovellus siitä, mihin viiva päättyy?
find_lines() on oikea työkalu, kun vastaus on ei. Viivaa seuraava robotti tarvitsee tietää mihin suuntaan viiva menee ja missä se ylittää kehyksen alareunan; viiva itse jatkuu horisonttiin ja sen yli. Horisonttitunnistin haluaa kuvan vahvimman suuntautuneen reunan; sen ei tarvitse tietää, mihin horisontti päättyy.
find_line_segments() on oikea työkalu, kun vastaus on kyllä. Painetun suorakulmion neljän sivun tunnistaminen tarvitsee neljä segmenttiä, joilla on tunnetut päätepisteet. Näyttöä osoittavan sormen seuraaminen tarkoittaa lyhyen segmentin seuraamista, jonka päätepisteet ovat sormen kärki ja tyvi. Näkyvän naarmun pituuden mittaaminen tarvitsee segmentin todellisen laajuuden pikseleinä.
Molemmilla tunnistimilla on yhteinen rajoitus: ne tarvitsevat kontrastia. Sobel-reunasuodatin, jolle ne rakentuvat, reagoi kirkkausgradientteihin; värillinen reuna yhtä kirkasta taustaa vasten (punainen viiva saman luminanssin vihreällä seinällä) ei tuota gradienttia eikä tunnistusta. Kun tuo tapaus ilmaantuu käytännössä, korjaus on poimia yksittäinen LAB-kanava harmaasävykuvaksi oikealla kontrastilla ennen hakua – to_grayscale() valittuna b-kanavalla erottaa punaisen vihreää vasten, missä luminanssikanava yksinään on tasainen – ja antaa tuo kanavakuva viivatunnistimelle.