5.17. Vakioytimien luettelo¶
Klassinen kuvankäsittely on kerännyt melko laajan luettelon kerneliytimien painokuvioista, jotka toistuvat yhä uudelleen – reunantunnistimet, teroittimet, kohokuvioinnit, pehmentimet, liike-epäterävyydet – ja jokainen niistä ajetaan morph()-metodin läpi. Jokainen on lyhyt, jokainen tekee yhden asian, ja useimmat ovat helppolukuisia, kun painojen peruslogiikka on selvinnyt.
Alla olevat ytimet ovat kaikki 3-kertaa-3 ellei toisin mainita, joten ne kaikki käyttävät kutsussa arvoa size=1. Kunkin ytimen painorakenne on kuvattu sen yhteydessä, sillä painojen lukeminen rakentaa intuition siitä, miksi yksi ydin kohokuvioi ja toinen teroittaa.
5.17.1. Identiteettiydin¶
Yksinkertaisin mahdollinen ydin on identiteetti – ykkönen keskellä, nolla kaikkialla muualla:
identity = [0, 0, 0,
0, 1, 0,
0, 0, 0]
img.morph(1, identity)
Kukin tuloskuvan pikseli ottaa arvonsa naapuruston keskeltä eli samassa kohdassa olevasta syötepikselistä. Kuva kulkee läpi muuttumattomana. Identiteetillä ei ole käytännön hyötyä suodattimena, mutta se on hyödyllinen lähtökohta kaikkien muiden ytimien ymmärtämiseen: mikä tahansa ei-identiteettinen ydin on identiteetti plus jokin muutos.
Ydin, jonka keskipaino on suuri ja sitä ympäröivät pienet negatiiviset painot, vähentää ympäryksen keskuksesta. Ydin, jonka keskipaino on nolla, jättää itse pikselin huomiotta ja reagoi vain naapureidensa välisiin eroihin. Ytimen lukeminen tällä tavalla – mitä keskipaino tekee pikselille, mitä ympäröivät painot lisäävät tai vievät – on nopein tapa ennustaa sen vaikutus.
5.17.2. Reunantunnistus¶
Reunantunnistus-ytimet reagoivat voimakkaasti kohtiin, joissa kirkkaus muuttuu nopeasti tiettyyn suuntaan, ja tuottavat lähes nollatuloksen siellä, missä kirkkaus on tasainen. Ne ovat perhe, jonka painot summautuvat nollaan: tasainen alue (jossa jokainen pikseli on saman arvoinen) tuottaa nollatuloksen, koska jokainen positiivinen paino kumoutuu täsmälleen yhtä suurella negatiivisella painolla.
Sobel-x on tämän kaanoninen esimerkki. Se tunnistaa pystysuuntaiset reunat (vasen/oikea-kirkkaussiirtymät):
sobel_x = [-1, 0, 1,
-2, 0, 2,
-1, 0, 1]
img.morph(1, sobel_x, mul=0.25, add=128)
Vastaava Sobel-y on sama kuvio 90 astetta kierrettynä; se tunnistaa vaakasuuntaiset reunat (ylä/ala-kirkkaussiirtymät):
sobel_y = [-1, -2, -1,
0, 0, 0,
1, 2, 1]
Sobel-x:n keskirivillä on painot -2 ja 2 eikä -1 ja 1. Keskirivin ylimääräinen paino antaa ytimelle pienen sisäänrakennetun pehmennyksen reunan suuntaisesti, mikä tekee siitä kohinaa vastaan kestävämmän kuin yksinkertaisempi Prewitt-operaattori, joka jättää nämä ylimääräiset suuruudet pois:
prewitt_x = [-1, 0, 1,
-1, 0, 1,
-1, 0, 1]
prewitt_y = [-1, -1, -1,
0, 0, 0,
1, 1, 1]
Prewitt painottaa jokaista riviä yhtä paljon, joten sen vaste on aavistuksen terävämpi kuin Sobelin, mutta hintana on suurempi herkkyys yhden pikselin kohinalle (ytimen ajamisen kustannus on identtinen – konvoluutio tekee saman työn painoista riippumatta). Puhtaalla kuvalla, jossa on voimakkaita reunoja, se on täysin käyttökelpoinen korvaaja Sobelille.
Scharr menee toiseen suuntaan. Sen painot ovat suurempia ja viritetty tarkkaan reunasuunnan tunnistukseen hienommissa kulmissa:
scharr_x = [-3, 0, 3,
-10, 0, 10,
-3, 0, 3]
img.morph(1, scharr_x, mul=0.0625, add=128)
Jakaja mul=0.0625 (1/16) tuo tuloksen takaisin välille 0 – 255 suuremman tulosumman jälkeen. Scharr on oikea valinta, kun sovellus tarvitsee geometrisesti uskollisimman gradienttivasteen ja on valmis maksamaan siitä hieman enemmän laskentaa.
5.17.3. Laplace-operaattori¶
Laplace-ydin reagoi reunoihin missä tahansa suunnassa kerralla. Siinä missä Sobelit tunnistavat kukin kirkkausmuutoksia yhden akselin suunnassa, Laplacen symmetrinen painokuvio reagoi samalla tavalla riippumatta siitä, mihin suuntaan reuna kulkee:
laplacian_4 = [ 0, -1, 0,
-1, 4, -1,
0, -1, 0]
img.morph(1, laplacian_4, add=128)
Rakenne: keskipaino 4, neljä vaaka-/pystysuuntaista naapuria painolla -1, neljä diagonaalia painolla nolla. Ydin summautuu nollaan, joten tasaiset alueet tuottavat nollatuloksen. Siellä missä kirkkaus muuttuu, keskiarvo poikkeaa neljän pääsuuntaisen naapurinsa keskiarvosta, ja tulos on tämän eron suuruus.
8-yhteyksisessä muunnoksessa ovat mukana diagonaaliset naapurit:
laplacian_8 = [-1, -1, -1,
-1, 8, -1,
-1, -1, -1]
Kukin ydin tunnistaa hieman eri asioita. 4-yhteyksinen versio tuottaa puhtaamman tuloksen vaaka- ja pystysuuntaisille reunoille; 8-yhteyksinen on isotrooppisempi – se reagoi yhtä hyvin joka suuntaan – mutta tuottaa hieman kohinaisemman tuloksen. 8-yhteyksinen ydin kiertää myös nimellä outline, sen reunojen visualisointiin käytön mukaan.
5.17.5. Kohokuviointi¶
Kohokuviointi-ydin tuottaa klassisista kuvankäsittelyohjelmista tutun sivulta valaistun vaikutelman. Tulos näyttää siltä kuin kuva olisi pursotettu reliefiksi ja sitten valaistu yhdestä kulmasta:
emboss = [-2, -1, 0,
-1, 1, 1,
0, 1, 2]
img.morph(1, emboss, add=128)
Kikka on epäsymmetria diagonaalin yli. Vasemmalla yläkulmalla on negatiivisin paino, oikealla alakulmalla positiivisin paino, ja kulmasta kulmaan kulkeva diagonaali etenee negatiivisesta yhden kautta positiiviseen. Kullakin pikselillä ydin laskee olennaisesti ”kirkkaus oikealla alapuolellani miinus kirkkaus vasemmalla yläpuolellani”, mikä on positiivinen siellä, missä kuva kirkastuu siihen suuntaan, ja negatiivinen siellä, missä se tummenee. Lisäämällä 128 etumerkillinen tulos keskitetään uudelleen keskiharmaaseen, jotta vaikutelma on näkyvissä.
Epäsymmetrian kiertäminen toisen diagonaalin yli kohokuvioi vastakkaisesta suunnasta:
emboss_alt = [ 0, 1, 2,
-1, 1, 1,
-2, -1, 0]
img.morph(1, emboss_alt, add=128)
Kaksi kohokuviointisuuntaa ovat hyödyllisiä yhdessä – vähentämällä toinen toisesta tai ajamalla kummatkin samalle kuvalle ja vertaamalla vasteita – kun sovelluksen täytyy tunnistaa orientaatio.
5.17.6. Pehmennys¶
Pehmennysytimet ovat perhe, jonka painot summautuvat yhteen (ja ovat kaikki ei-negatiivisia). Tasainen alue tällaisen ytimen läpi tuottaa saman tasaisen kirkkauden, koska ydin keskiarvoistaa pikseliarvot yhteen sen sijaan että vahvistaisi niiden eroja.
Yksinkertaisin on laatikkosumennus (box blur), joka on täsmälleen se mitä mean() laskee:
box_blur = [1, 1, 1,
1, 1, 1,
1, 1, 1]
img.morph(1, box_blur)
Ydin summautuu arvoon 9, joten automaattinen jako ytimen summalla muuttaa tulosumman todelliseksi keskiarvoksi naapuruston yhdeksästä pikselistä. Käytännössä mean() on parempi tapa ajaa tämä ydin – se tuottaa saman tuloksen nopeammin, polun kautta joka on optimoitu pelkästään keskiarvon laskentaan, kun taas morph ajaa yleisen konvoluutiokoneiston. Laatikkosumennus on luettelossa siksi, että se on oikea lähtökohta kaikkien muiden pehmennysytimien ymmärtämiseen.
Gaussin 3-kertaa-3-approksimaatio painottaa keskustaa ja pääsuuntaisia naapureita enemmän kuin kulmia:
gaussian = [1, 2, 1,
2, 4, 2,
1, 2, 1]
img.morph(1, gaussian)
Painot ovat Pascalin kolmion rivi 1, 2, 1 ulkotulona itsensä kanssa. Keskipaino 4 on suurin, koska keskipikseli vaikuttaa eniten omaan tulokseensa; kulmat ovat 1, koska ne ovat kauimpana keskuksesta. Ydin summautuu arvoon 16, ja automaattinen jako ytimen summalla hoitaa normalisoinnin – mul-argumenttia ei tarvita. 3-kertaa-3-muoto on karkea approksimaatio todellisesta Gaussista, eikä erotu gaussian()-metodista arvolla size=1; morph-muoto on enimmäkseen hyödyllinen, kun sovellus haluaa yhdistää pehmennyksen toiseen operaatioon samalla läpiajolla.
5.17.7. Liike-epäterävyys¶
Liike-epäterävyys-ydin keskiarvoistaa pikseleitä yhteen suuntaan jättäen kohtisuoran suunnan terävöittämättä. Yksinkertaisin tapaus on vaakasuuntainen:
motion_h = [0, 0, 0,
1, 1, 1,
0, 0, 0]
img.morph(1, motion_h)
Keskirivi keskiarvoistaa kolme pikseliä vaaka-akselin suunnassa; ylä- ja alarivit ovat nollia. Ydin summautuu arvoon 3, joten automaattinen jako ytimen summalla tuottaa todellisen kolmen pikselin keskiarvon ilman mitään mul-tarvetta. Tulos on syötteen vaakasuuntaisesti levitetty kopio – vaikutelma, jonka kamera tallentaa kun kohde liikkuu sivusuunnassa valotuksen aikana. Pystysuuntainen liike-epäterävyys on sama kuvio kierrettynä:
motion_v = [0, 1, 0,
0, 1, 0,
0, 1, 0]
Diagonaalinen liike-epäterävyys käyttää päädiagonaalia:
motion_diag = [1, 0, 0,
0, 1, 0,
0, 0, 1]
img.morph(1, motion_diag)
Liike-epäterävyysytimet ovat hyödyllisiä sekä tehosteena (kehyksen tarkoituksellinen sumentaminen visuaalisista syistä) että testikuviona algoritmeille, joiden täytyy kestää liikeartefakteja (aja algoritmi liike-epäterävälle syötteelle ja tarkista, että se tuottaa edelleen oikean vastauksen).
5.17.8. Ytimien lukeminen yhdellä silmäyksellä¶
Muutama nyrkkisääntö tekee uusien ytimien lukemisesta helpompaa näkemältä:
Summa yhteen ei-negatiivisilla painoilla ⇒ pehmennys (säilyttää keskimääräisen kirkkauden).
Summa nollaan sekä positiivisilla että negatiivisilla painoilla ⇒ reunavaste (nolla tasaisilla alueilla).
Summa yhteen suurella positiivisella keskuksella ja pienillä negatiivisilla ympäryksillä ⇒ teroitus (identiteetti plus reunavaste).
Epäsymmetrinen diagonaalin yli summan ollessa yksi ⇒ kohokuviointi (korostaa jokaisen kirkkaussiirtymän toista puolta).
Keskittynyt yhden akselin suuntaan summan ollessa yksi ⇒ suuntainen sumennus.
Ensimmäinen näistä, johon ydin täsmää, on yleensä oikea arvaus siitä mitä se tekee. Useimmat hyödylliset ytimet tunnistaa pelkästään niiden painokuvion asettelusta.
Kun mikään vakioytimistä ei tee sitä mitä sovellus haluaa, seuraava askel on virittää sellainen käsin. Yllä olevien sääntöjen sekä mul / add -säätimien yhdistelmä kattaa lähes jokaisen lineaarisen läpiajon, jota klassinen konenäköputki on koskaan halunnut; siitä eteenpäin on kyse painojen kokeilemisesta, tuloksen tarkastelusta ja iteroinnista.