5.16. Nuclee de convoluție personalizate¶
Filtrele de vecinătate prezentate până acum aveau fiecare o statistică integrată pe care filtrul o aplica ferestrei în fiecare poziție – media, media ponderată gaussian, mediana. morph() este singurul filtru care permite aplicației să furnizeze ea însăși statistica, sub forma unui nucleu: o matrice mică de ponderi care descrie modul în care filtrul ar trebui să combine pixelii din vecinătate într-o singură valoare de ieșire.
Mecanismul este operația clasică de convoluție. În fiecare poziție de ieșire, fiecare pixel din vecinătate este înmulțit cu ponderea corespunzătoare din nucleu, produsele sunt însumate, rezultatul este opțional scalat și deplasat, iar valoarea este scrisă în pixelul de ieșire. Nuclee diferite produc rezultate diferite din aceeași intrare. Un nucleu cu toate ponderile pozitive egale reproduce filtrul mean(); unul în formă de clopot reproduce gaussian(). Modelele dincolo de acestea produc răspunsuri la muchii, reliefări, gradienți, accentuare, neclaritate de mișcare și un lung catalog de alte efecte – tot ceea ce procesarea clasică a imaginilor și-a dorit vreodată să facă într-o singură trecere liniară.
5.16.1. Metoda morph¶
Semnătura arată ca celelalte filtre de vecinătate, cu un argument suplimentar:
img.morph(size, kernel, mul=1.0, add=0.0)
size este raza, la fel ca peste tot, deci nucleul trebuie să aibă exact (2 * size + 1) rânduri pe (2 * size + 1) coloane. Nucleul în sine este o listă Python plată cu acest număr de valori, în ordine pe rânduri (row-major) – primele (2 * size + 1) valori sunt rândul de sus, următoarele (2 * size + 1) sunt al doilea rând și așa mai departe, până la rândul de jos. mul scalează suma produselor înainte ca aceasta să fie scrisă în pixelul de ieșire, iar add adaugă o constantă. Valorile implicite mul=1.0 și add=0.0 lasă rezultatul convoluției neschimbat.
Un detaliu care merită explicitat: metoda împarte automat suma produselor la suma valorilor nucleului înainte de a scrie ieșirea. Această împărțire automată înseamnă că un nucleu de mediere ale cărui valori se însumează la nouă – o neclaritate de tip box 3 pe 3, de exemplu – iese la scara de o noime fără efort suplimentar, iar un nucleu de aproximare gaussiană care se însumează la șaisprezece iese la scara de o șaisprezecime, ambele fără ca aplicația să fie nevoită să calculeze ea însăși împărțirea. Aplicația setează mul doar atunci când dorește o scalare suplimentară peste auto-normalizare – sau, mai frecvent, atunci când nucleul se însumează la zero (un nucleu de răspuns la muchii) și împărțirea automată ar fi o împărțire la nimic. În acest caz, cadrul tratează suma ca fiind unu, iar mul devine singurul buton pentru a menține în interval suma nescalată a produselor.
Perechea threshold=True / offset=N din secțiunea despre pragul adaptiv funcționează și cu morph(), astfel încât același cadru de nuclee personalizate poate produce un prag binar al cărui prag de tăiere este calculat de o statistică personalizată.
5.16.2. Dispunerea nucleului¶
Un nucleu 3 pe 3 (size=1) este o listă plată de nouă numere dispuse de la stânga la dreapta, de sus în jos. Convenția se citește natural dacă lista este împărțită pe trei linii Python:
sobel_x = [-1, 0, 1,
-2, 0, 2,
-1, 0, 1]
Acesta este operatorul de gradient Sobel-x – primul nucleu standard pe care orice aplicație îl va dori și unul util de parcurs de la cap la coadă. Modelul este simplu: ponderi negative pe coloana din stânga, ponderi pozitive pe coloana din dreapta, cu coloana centrală zero. Ponderile de rând -1, -2, -1 (sau 1, 2, 1 în dreapta) sunt mai mari la mijloc decât la colțuri, ceea ce conferă rândului central mai multă influență asupra rezultatului decât rândurilor de la colțuri.
Când nucleul mătură o muchie verticală – o coloană de pixeli care trece de la întunecat în stânga la luminos în dreapta – ponderile negative captează partea întunecată, iar ponderile pozitive captează partea luminoasă. Suma produselor este un număr pozitiv mare, pe care filtrul îl scrie ca pixel de ieșire luminos. O zonă orizontală de luminozitate uniformă produce zero, deoarece fiecare pondere pozitivă este echilibrată de o pondere negativă de aceeași magnitudine pe un pixel cu aceeași valoare.
Rularea nucleului:
img.morph(1, sobel_x, mul=0.25)
Nucleul Sobel se însumează la zero – fiecare pondere negativă din partea stângă este echilibrată de o pondere pozitivă egală din dreapta – astfel încât împărțirea automată nu împarte la nimic, iar mul este singura scalare aplicată sumei produselor. mul=0.25 menține răspunsul în interval: cea mai mare sumă absolută pe care Sobel-x o poate produce dintr-o zonă 3 pe 3 este aproximativ 4 * 255 = 1020 (opt pixeli luminoși ponderați până la 2), iar împărțirea acesteia la patru aduce cazurile extreme la 255, unde formatul le decupează curat.
Nucleul Sobel-y corespunzător detectează muchiile orizontale prin rotirea aceluiași model de ponderi cu 90 de grade:
sobel_y = [-1, -2, -1,
0, 0, 0,
1, 2, 1]
Aplicațiile care doresc să detecteze orice muchie, indiferent de direcție, rulează de obicei ambele filtre Sobel și combină răspunsurile.
5.16.3. Deplasarea ieșirii¶
add este cealaltă jumătate a poveștii despre scalare. Răspunsul unui nucleu cu sumă zero este cu semn – pozitiv pe o parte a unei muchii, negativ pe cealaltă – iar jumătatea negativă se decupează la zero atunci când este scrisă într-un pixel fără semn. add=128 deplasează răspunsul pentru a fi centrat pe gri mediu, astfel încât răspunsurile negative supraviețuiesc ca valori sub 128, iar cele pozitive ajung deasupra: un răspuns la muchii sau o reliefare devine vizibilă în ambele direcții, cu prețul a jumătate din interval în fiecare.
Ce combinație de mul și add așteaptă un nucleu face parte din designul nucleului; catalogul de nuclee standard enumeră setările corecte pentru fiecare nucleu comun.
5.16.4. Nuclee mai mari¶
Tot ce este pe această pagină a fost descris cu nuclee 3 pe 3 (size=1), deoarece aceasta este dimensiunea folosită de catalogul standard și pentru că dispunerea pe rânduri este ușor de scris de mână la această dimensiune. Totuși, nimic din mecanism nu restricționează nucleul la 3 pe 3. size=2 rulează un nucleu 5 pe 5, cu douăzeci și cinci de valori în lista plată; size=3 rulează unul 7 pe 7 cu patruzeci și nouă; și așa mai departe, până la orice rază pe care aplicația este dispusă să o plătească. Cadrul gestionează atât dispunerea de tip listă plată, cât și cea de rânduri imbricate, la orice dimensiune impară.
Motivul pentru a apela la un nucleu mai mare este același cu motivul pentru a apela la o vecinătate mai mare la oricare dintre filtrele integrate: mai multă mediere, detectare mai amplă a caracteristicilor, sensibilitate mai mică la zgomotul de un singur pixel. Costul crește proporțional cu pătratul razei – un nucleu 5 pe 5 efectuează aproximativ de 2,8 ori lucrul pe pixel al unuia 3 pe 3, iar unul 7 pe 7 de aproximativ 5,4 ori – iar acest multiplicator iese direct din rata de cadre.
Modelul practic este să rămâneți la size=1 pentru catalogul standard și să apelați la dimensiuni mai mari doar atunci când algoritmul are nevoie de o vecinătate mai mare. Detectoarele de muchii beneficiază rareori dincolo de 3 pe 3; filtrele de netezire o fac uneori; dimensiunea potrivită depinde de scara caracteristicilor pe care aplicația încearcă să le evidențieze sau să le suprime.
5.16.5. Când să apelați la morph¶
Pentru netezirea de zi cu zi, mean(), gaussian() și bilateral() sunt mai rapide și mai curate. Pentru detectarea muchiilor, laplacian() și find_edges() sunt construite special pentru acest scop. Argumentul pentru a apela direct la morph() apare atunci când aplicația are nevoie de o convoluție specifică pe care filtrele integrate nu o expun – un Sobel direcțional, un șablon de muchie personalizat, un nucleu reglat pentru o anumită textură pe care restul fluxului de procesare o va căuta, sau oricare dintre nucleele utile din catalogul standard pe care procesarea clasică a imaginilor le-a acumulat de-a lungul deceniilor. Flexibilitatea completă a nucleelor arbitrare este disponibilă; prețul este că aplicația este responsabilă pentru alegerea valorilor nucleului care produc rezultatul dorit.