5.16. Prilagođene konvolucijske jezgre¶
Svaki od dosad opisanih filtara susjedstva imao je ugrađenu statistiku koju je filtar primjenjivao na prozor u svakom položaju – srednju vrijednost, Gaussovom funkcijom ponderirani prosjek, medijan. morph() je jedini filtar koji aplikaciji dopušta da sama dostavi statistiku, u obliku jezgre: male matrice težina koja opisuje kako filtar treba spojiti piksele susjedstva u jednu izlaznu vrijednost.
Mehanizam je klasična operacija konvolucije. Na svakom izlaznom položaju svaki se piksel susjedstva množi s odgovarajućom težinom u jezgri, umnošci se zbrajaju, rezultat se po želji skalira i pomiče, a vrijednost se zapisuje u izlazni piksel. Različite jezgre daju različite rezultate iz istog ulaza. Jezgra sa svim jednakim pozitivnim težinama reproducira filtar mean(); jezgra u obliku zvona reproducira gaussian(). Uzorci osim tih daju odzive rubova, reljefe, gradijente, izoštravanje, zamućenje pokretom i dugačak katalog drugih efekata – sve što je klasična obrada slike ikad željela učiniti jednim linearnim prolazom.
5.16.1. Metoda morph¶
Potpis izgleda kao kod drugih filtara susjedstva, uz jedan dodatni argument:
img.morph(size, kernel, mul=1.0, add=0.0)
size je radijus na isti način kao i svugdje drugdje, pa jezgra mora imati točno (2 * size + 1) redaka i (2 * size + 1) stupaca. Sama jezgra je ravna Python lista s tim brojem brojeva, u redoslijedu po recima (row-major) – prvih (2 * size + 1) unosa je gornji redak, sljedećih (2 * size + 1) je drugi redak, i tako dalje, sve do donjeg retka. mul skalira zbroj umnožaka prije nego što se zapiše u izlazni piksel, a add dodaje konstantu. Zadane vrijednosti mul=1.0 i add=0.0 ostavljaju izlaz konvolucije nepromijenjenim.
Jedan detalj koji vrijedi izričito naglasiti: metoda automatski dijeli zbroj umnožaka sa zbrojem unosa jezgre prije zapisivanja izlaza. Ta automatska podjela znači da jezgra za usrednjavanje čiji unosi u zbroju daju devet – na primjer 3-puta-3 box zamućenje – izlazi u jednoj devetini razmjera bez ikakvog dodatnog truda, a jezgra za Gaussovu aproksimaciju čiji zbroj iznosi šesnaest izlazi u jednoj šesnaestini razmjera, oboje bez da aplikacija mora sama računati podjelu. Aplikacija postavlja mul samo kada želi dodatno skaliranje povrh automatske normalizacije – ili, češće, kada zbroj jezgre iznosi nulu (jezgra za odziv rubova) pa bi automatska podjela bila dijeljenje ničim. Okvir u tom slučaju tretira zbroj kao jedan, a mul postaje jedina ručica za održavanje neskaliranog zbroja umnožaka unutar raspona.
Par threshold=True / offset=N iz odjeljka o prilagodljivom pragu također radi na morph(), pa isti okvir za prilagođene jezgre može proizvesti binarni prag čija se granica računa prilagođenom statistikom.
5.16.2. Raspored jezgre¶
Jezgra 3-puta-3 (size=1) je ravna lista od devet brojeva poredanih s lijeva na desno, od vrha prema dnu. Konvencija se prirodno čita ako se lista razlomi na tri Python retka:
sobel_x = [-1, 0, 1,
-2, 0, 2,
-1, 0, 1]
Ovo je gradijentni operator Sobel-x – prva standardna jezgra koju će svaka aplikacija htjeti i korisna za detaljan prikaz od početka do kraja. Uzorak je jednostavan: negativne težine u lijevom stupcu, pozitivne težine u desnom stupcu, sa središnjim stupcem nula. Težine redaka -1, -2, -1 (ili 1, 2, 1 na desnoj strani) veće su u sredini nego u kutovima, što središnjem retku daje veći utjecaj na rezultat nego kutnim recima.
Kada jezgra prelazi preko okomitog ruba – stupca piksela koji ide od tamnog s lijeve strane do svijetlog s desne – negativne težine hvataju tamnu stranu, a pozitivne težine svijetlu stranu. Zbroj umnožaka je velik pozitivan broj, koji filtar zapisuje kao svijetli izlazni piksel. Vodoravna mrlja ujednačene svjetline daje nulu, jer je svaka pozitivna težina uparena s negativnom težinom iste veličine na pikselu iste vrijednosti.
Pokretanje jezgre:
img.morph(1, sobel_x, mul=0.25)
Sobelova jezgra u zbroju daje nulu – svaka negativna težina na lijevoj strani uparena je s jednakom pozitivnom težinom na desnoj – pa automatska podjela ničim ne dijeli, a mul je jedino skaliranje zbroja umnožaka. mul=0.25 zadržava odziv u rasponu: najveći apsolutni zbroj koji Sobel-x može proizvesti iz mrlje 3-puta-3 iznosi otprilike 4 * 255 = 1020 (osam svijetlih piksela ponderiranih do 2), a dijeljenje toga s četiri smješta ekstremne slučajeve na 255, gdje ih format uredno odrezuje.
Odgovarajuća jezgra Sobel-y detektira vodoravne rubove rotirajući isti uzorak težina za 90 stupnjeva:
sobel_y = [-1, -2, -1,
0, 0, 0,
1, 2, 1]
Aplikacije koje žele detektirati bilo koji rub, bez obzira na smjer, obično pokreću oba Sobela i kombiniraju odzive.
5.16.3. Pomicanje izlaza¶
add je druga polovica priče o skaliranju. Odziv jezgre čiji zbroj iznosi nulu je predznačen – pozitivan s jedne strane ruba, negativan s druge – a negativna polovica se odrezuje na nulu kada se zapiše u nepredznačen piksel. add=128 pomiče odziv tako da bude centriran na srednje sivu, pa negativni odzivi preživljavaju kao vrijednosti ispod 128, a pozitivni završavaju iznad nje: odziv ruba ili reljef postaje vidljiv u oba smjera, po cijenu polovice raspona u svakom.
Koju kombinaciju mul i add jezgra očekuje dio je dizajna te jezgre; katalog standardnih jezgri navodi ispravne postavke za svaku uobičajenu jezgru.
5.16.4. Veće jezgre¶
Sve na ovoj stranici opisano je jezgrama 3-puta-3 (size=1), jer je to veličina koju koristi standardni katalog i jer je raspored po recima lako ručno ispisati pri toj veličini. Ništa u mehanizmu, međutim, ne ograničava jezgru na 3-puta-3. size=2 pokreće jezgru 5-puta-5, s dvadeset i pet unosa u ravnoj listi; size=3 pokreće jezgru 7-puta-7 s četrdeset i devet; i tako dalje, sve do bilo kojeg radijusa koji je aplikacija spremna platiti. Okvir obrađuje raspored ravne liste ili ugniježđenih redaka pri bilo kojoj neparnoj veličini.
Razlog za posezanje za većom jezgrom isti je kao i razlog za posezanje za većim susjedstvom kod bilo kojeg ugrađenog filtra: više usrednjavanja, šira detekcija značajki, manja osjetljivost na šum pojedinačnog piksela. Trošak raste s kvadratom radijusa – 5-puta-5 obavlja otprilike 2,8 puta više rada po pikselu od 3-puta-3, a 7-puta-7 oko 5,4 puta – i taj množitelj izravno smanjuje broj sličica u sekundi.
Praktičan obrazac je ostati pri size=1 za standardni katalog i posezati za većim veličinama samo kada algoritmu treba veće susjedstvo. Detektori rubova rijetko imaju koristi izvan 3-puta-3; filtri za zaglađivanje ponekad imaju; ispravna veličina ovisi o mjerilu značajki koje aplikacija pokušava naglasiti ili potisnuti.
5.16.5. Kada posegnuti za morph¶
Za svakodnevno zaglađivanje mean(), gaussian() i bilateral() brži su i čišći. Za detekciju rubova laplacian() i find_edges() namjenski su izrađeni. Razlog za izravno posezanje za morph() jest kada aplikaciji treba specifična konvolucija koju ugrađeni filtri ne izlažu – usmjereni Sobel, prilagođeni predložak ruba, jezgra podešena za određenu teksturu koju će ostatak cjevovoda tražiti, ili bilo koja od standardnog kataloga korisnih jezgri koje je klasična obrada slike izgradila tijekom desetljeća. Dostupna je puna fleksibilnost proizvoljnih jezgri; cijena je da je aplikacija odgovorna za odabir vrijednosti jezgre koje daju željeni rezultat.