5.17. Katalog kernel standar¶
Pemrosesan citra klasik telah mengumpulkan katalog pola bobot kernel yang cukup banyak dan terus muncul berulang kali -- detektor tepi, penajam, emboss, penghalus, motion blur -- dan semuanya dijalankan melalui morph(). Masing-masing singkat, melakukan satu hal, dan sebagian besar mudah dipahami begitu logika dasar bobot-bobotnya dipahami.
Kernel-kernel di bawah ini semuanya berukuran 3-kali-3 kecuali disebutkan lain, sehingga semuanya menggunakan size=1 dalam pemanggilan. Struktur bobot setiap kernel dijelaskan di sampingnya, karena membaca bobot-bobotnya adalah cara membangun intuisi mengapa satu kernel menghasilkan efek emboss dan yang lain menajamkan.
5.17.1. Kernel identitas¶
Kernel paling sederhana yang mungkin adalah kernel identitas -- satu di tengah, nol di mana-mana:
identity = [0, 0, 0,
0, 1, 0,
0, 0, 0]
img.morph(1, identity)
Setiap piksel keluaran mengambil nilainya dari tengah lingkungan, yang merupakan piksel masukan di posisi yang sama. Citra melewatinya tanpa perubahan. Identitas tidak memiliki kegunaan praktis sebagai filter, tetapi merupakan baseline yang berguna untuk memahami setiap kernel lainnya: setiap kernel non-identitas adalah identitas ditambah beberapa modifikasi.
Sebuah kernel yang bobot pusatnya besar dengan bobot negatif kecil di sekitarnya mengurangi lingkungan dari pusat. Kernel dengan bobot pusat nol mengabaikan piksel itu sendiri dan hanya merespons perbedaan di antara tetangganya. Membaca kernel dengan cara ini -- apa yang dilakukan bobot pusat terhadap piksel, apa yang ditambahkan atau dikurangi oleh bobot sekitarnya -- adalah cara tercepat untuk memprediksi efeknya.
5.17.2. Deteksi tepi¶
Kernel deteksi tepi merespons kuat terhadap posisi di mana kecerahan berubah dengan cepat dalam arah tertentu, dan menghasilkan keluaran mendekati nol di mana kecerahan seragam. Ini adalah keluarga yang jumlah bobotnya nol: sebuah area datar (setiap piksel memiliki nilai yang sama) menghasilkan keluaran nol, karena setiap bobot positif dibatalkan tepat oleh bobot negatif yang besarnya sama.
Sobel-x adalah contoh kanonik. Ia mendeteksi tepi vertikal (transisi kecerahan kiri/kanan):
sobel_x = [-1, 0, 1,
-2, 0, 2,
-1, 0, 1]
img.morph(1, sobel_x, mul=0.25, add=128)
Sobel-y yang sesuai adalah pola yang sama diputar 90 derajat; ia mendeteksi tepi horizontal (transisi kecerahan atas/bawah):
sobel_y = [-1, -2, -1,
0, 0, 0,
1, 2, 1]
Baris tengah Sobel-x memiliki bobot -2 dan 2 alih-alih -1 dan 1. Bobot ekstra pada baris tengah memberikan kernel penghalusan bawaan kecil dalam arah sepanjang tepi, yang membuatnya lebih tahan terhadap noise daripada operator Prewitt yang lebih sederhana yang menghilangkan besaran ekstra tersebut:
prewitt_x = [-1, 0, 1,
-1, 0, 1,
-1, 0, 1]
prewitt_y = [-1, -1, -1,
0, 0, 0,
1, 1, 1]
Prewitt memberi bobot yang sama pada setiap baris, sehingga responsnya sedikit lebih tajam dari Sobel, dengan risiko lebih sensitif terhadap noise piksel tunggal (biaya menjalankan kernel identik -- konvolusi melakukan pekerjaan yang sama apa pun bobotnya). Pada citra bersih dengan tepi yang kuat, ini adalah pengganti yang sangat memadai untuk Sobel.
Scharr mengambil arah yang berlawanan. Bobotnya lebih besar dan disetel untuk deteksi arah tepi yang akurat pada sudut-sudut yang lebih halus:
scharr_x = [-3, 0, 3,
-10, 0, 10,
-3, 0, 3]
img.morph(1, scharr_x, mul=0.0625, add=128)
Pembagi mul=0.0625 (1/16) membawa keluaran kembali ke dalam rentang 0 -- 255 setelah jumlah perkalian yang lebih besar. Scharr adalah jawaban yang tepat ketika aplikasi memerlukan respons gradien yang paling setia secara geometris dan bersedia membayar sedikit lebih banyak komputasi untuk itu.
5.17.3. Laplacian¶
Kernel Laplacian merespons tepi dalam semua arah sekaligus. Di mana masing-masing Sobel mendeteksi perubahan kecerahan sepanjang satu sumbu, pola bobot simetris Laplacian merespons dengan cara yang sama terlepas dari arah tepinya:
laplacian_4 = [ 0, -1, 0,
-1, 4, -1,
0, -1, 0]
img.morph(1, laplacian_4, add=128)
Strukturnya: bobot pusat 4, empat tetangga horizontal/vertikal berbobot -1, empat diagonal berbobot nol. Kernel berjumlah nol, sehingga area datar menghasilkan keluaran nol. Di mana kecerahan berubah, nilai pusat berbeda dari rata-rata empat tetangga kardinalnya, dan keluarannya adalah besarnya perbedaan tersebut.
Varian 8-terhubung menyertakan tetangga diagonal:
laplacian_8 = [-1, -1, -1,
-1, 8, -1,
-1, -1, -1]
Setiap kernel mendeteksi hal yang sedikit berbeda. Versi 4-terhubung menghasilkan keluaran yang lebih bersih pada tepi horizontal dan vertikal; yang 8-terhubung lebih isotropik -- merespons dengan baik dalam setiap arah -- tetapi menghasilkan keluaran yang sedikit lebih berisik. Kernel 8-terhubung juga beredar dengan nama outline, setelah penggunaannya untuk memvisualisasikan tepi.
5.17.5. Emboss¶
Kernel emboss menghasilkan efek pencahayaan dari samping yang ditemukan di editor citra klasik. Keluarannya terlihat seperti citra diekstrusi menjadi relief dan kemudian diterangi dari satu sudut:
emboss = [-2, -1, 0,
-1, 1, 1,
0, 1, 2]
img.morph(1, emboss, add=128)
Triknya adalah asimetri di sepanjang diagonal. Sudut kiri atas memiliki bobot paling negatif, sudut kanan bawah memiliki bobot paling positif, dan diagonal dari sudut ke sudut berjalan dari negatif melalui satu ke positif. Pada setiap piksel, kernel pada dasarnya menghitung "kecerahan di kanan bawah saya dikurangi kecerahan di kiri atas saya," yang positif di mana citra menjadi lebih cerah dalam arah tersebut dan negatif di mana menjadi lebih gelap. Menambahkan 128 memusatkan kembali keluaran bertanda ke abu-abu tengah sehingga efeknya terlihat.
Memutar asimetri di sepanjang diagonal lainnya menghasilkan emboss dari arah yang berlawanan:
emboss_alt = [ 0, 1, 2,
-1, 1, 1,
-2, -1, 0]
img.morph(1, emboss_alt, add=128)
Dua arah emboss berguna dalam kombinasi -- mengurangi satu dari yang lain, atau menjalankan masing-masing pada citra yang sama dan membandingkan responsnya -- ketika suatu aplikasi perlu mendeteksi orientasi.
5.17.6. Penghalusan¶
Kernel penghalusan adalah keluarga yang jumlah bobotnya satu (dan semuanya non-negatif). Area datar melalui kernel seperti itu menghasilkan kecerahan datar yang sama, karena kernel merata-ratakan nilai piksel bersama alih-alih memperkuat perbedaannya.
Yang paling sederhana adalah box blur, yang persis seperti yang dihitung oleh mean():
box_blur = [1, 1, 1,
1, 1, 1,
1, 1, 1]
img.morph(1, box_blur)
Kernel berjumlah 9, sehingga pembagian otomatis dengan jumlah kernel mengubah jumlah perkalian menjadi rata-rata sejati atas sembilan piksel lingkungan. Dalam praktiknya mean() adalah cara yang lebih baik untuk menjalankan kernel ini -- menghasilkan keluaran yang sama lebih cepat, melalui jalur yang dioptimalkan untuk menghitung rata-rata dan tidak ada yang lain, sedangkan morph menjalankan mesin konvolusi umum. Box blur ada dalam katalog karena merupakan baseline yang tepat untuk memahami setiap kernel penghalusan lainnya.
Aproksimasi 3-kali-3 dari bobot Gaussian memberi lebih banyak bobot pada pusat dan tetangga kardinal daripada sudut-sudut:
gaussian = [1, 2, 1,
2, 4, 2,
1, 2, 1]
img.morph(1, gaussian)
Bobotnya adalah baris segitiga Pascal 1, 2, 1 yang dikalikan luar dengan dirinya sendiri. Bobot pusat 4 adalah yang terbesar karena piksel pusat paling banyak berkontribusi pada keluarannya sendiri; sudut-sudutnya adalah 1 karena paling jauh dari pusat. Kernel berjumlah 16, dan pembagian otomatis dengan jumlah kernel menangani normalisasi -- tidak diperlukan argumen mul. Bentuk 3-kali-3 adalah aproksimasi kasar dari Gaussian sejati dan tidak dapat dibedakan dari gaussian() pada size=1; bentuk morph sebagian besar berguna ketika suatu aplikasi ingin menggabungkan penghalusan dengan operasi lain dalam satu pass yang sama.
5.17.7. Motion blur¶
Kernel motion blur merata-ratakan piksel sepanjang satu arah, membiarkan arah tegak lurusnya tidak kabur. Kasus paling sederhana adalah horizontal:
motion_h = [0, 0, 0,
1, 1, 1,
0, 0, 0]
img.morph(1, motion_h)
Baris tengah merata-ratakan tiga piksel sepanjang sumbu horizontal; baris atas dan bawah adalah nol. Kernel berjumlah 3, sehingga pembagian otomatis dengan jumlah kernel menghasilkan rata-rata tiga piksel yang sejati tanpa mul yang diperlukan. Keluarannya adalah salinan masukan yang dikaburkan secara horizontal -- efek yang ditangkap kamera ketika subjek bergerak ke samping selama eksposur. Motion blur vertikal adalah pola yang sama diputar:
motion_v = [0, 1, 0,
0, 1, 0,
0, 1, 0]
Motion blur diagonal menggunakan diagonal utama:
motion_diag = [1, 0, 0,
0, 1, 0,
0, 0, 1]
img.morph(1, motion_diag)
Kernel motion blur berguna baik sebagai efek (mengaburkan bingkai secara sengaja untuk tujuan visual) maupun sebagai pola uji untuk algoritma yang perlu tahan terhadap artefak gerakan (jalankan algoritma pada masukan yang dikaburkan oleh gerakan dan periksa bahwa algoritma masih menghasilkan jawaban yang benar).
5.17.8. Membaca kernel secara sekilas¶
Beberapa aturan praktis membuat kernel baru lebih mudah dibaca secara sekilas:
Jumlah satu dengan bobot non-negatif ⇒ penghalusan (mempertahankan kecerahan rata-rata).
Jumlah nol dengan bobot positif dan negatif ⇒ respons tepi (nol pada area datar).
Jumlah satu dengan pusat positif besar dan lingkungan negatif kecil ⇒ penajaman (identitas ditambah respons tepi).
Asimetris di sepanjang diagonal dengan jumlah satu ⇒ embossing (menyoroti satu sisi setiap transisi kecerahan).
Terkonsentrasi sepanjang satu sumbu dengan jumlah satu ⇒ blur terarah.
Yang pertama dari ini yang cocok dengan kernel biasanya merupakan tebakan yang tepat tentang apa yang dilakukannya. Sebagian besar kernel yang berguna dapat dikenali dari tata letak pola bobotnya saja.
Ketika tidak ada kernel standar yang melakukan apa yang diinginkan aplikasi, langkah selanjutnya adalah menyetelnya secara manual. Kombinasi aturan-aturan di atas dan kontrol mul / add mencakup hampir setiap pass linier yang pernah diinginkan oleh pipeline visi mesin klasik; dari sana ini hanyalah masalah mencoba bobot-bobot, melihat keluarannya, dan melakukan iterasi.