5.26. Menemukan garis dan segmen¶
Beberapa fitur pemandangan bukan merupakan wilayah warna yang terhubung melainkan tepi lurus yang berorientasi: garis cat di lantai, sambungan antara dua permukaan, sisi persegi panjang cetak, tepi pintu. Meminta detektor blob untuk menemukannya adalah pertanyaan yang salah -- tepi itu lebarnya satu piksel, algoritma blob menginginkan area-dengan-warna, dan jawabannya akan kembali kosong atau berisik.
Detektor yang tepat untuk tepi berorientasi adalah transformasi garis Hough. Modul image menyediakannya dalam dua versi: find_lines() mengembalikan garis tak terbatas (setiap garis memanjang melintasi seluruh citra); find_line_segments() mengembalikan segmen terbatas (setiap garis memiliki titik ujung di dalam bingkai). Mana yang dibutuhkan aplikasi bergantung pada apakah tepi yang diminati bersambung melintasi seluruh bingkai atau hanya mencakup sebagian darinya.
5.26.1. Cara kerja transformasi Hough¶
Kedua detektor berbagi ide inti yang sama, sehingga memahaminya sekali sudah cukup. Modul image pertama-tama menjalankan filter tepi bergaya Sobel pada input untuk memberi skor setiap piksel berdasarkan seberapa mungkin piksel tersebut berada pada tepi berorientasi. Setiap piksel tepi tersebut kemudian memberikan suara untuk semua garis yang mungkin dilewatinya. Garis yang mengumpulkan suara terbanyak menang.
Sebuah garis diparameterisasi dalam ruang Hough dengan dua angka: theta, sudut garis (0 -- 179 derajat), dan rho, jarak tegak lurus dari asal citra ke garis (bertanda, dalam piksel). Setiap garis yang ada dalam citra adalah satu titik dalam ruang (theta, rho). Setiap piksel tepi pada input memberikan satu suara untuk setiap kombinasi (theta, rho) yang konsisten dengan posisinya -- secara konseptual, sebuah kurva melalui ruang Hough. Di mana banyak kurva seperti itu bersilangan, banyak piksel tepi menyepakati garis yang sama, dan persilangan itu adalah sebuah deteksi.
Detektor mengembalikan maksima lokal dalam ruang Hough yang total suaranya melampaui sebuah ambang batas. Setiap Line yang dikembalikan membawa kedua representasi: x1, y1, x2, y2 untuk bentuk titik ujung (dipotong ke batas citra untuk kasus tak terbatas), theta, rho untuk bentuk Hough, serta length dan magnitude masing-masing untuk ukuran dan jumlah suara.
5.26.2. Garis tak terbatas¶
find_lines() menjalankan transformasi Hough dan mengembalikan garis-garis terkuat, masing-masing diperluas melintasi seluruh citra:
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 adalah total suara minimum agar sebuah garis diterima. Total suara menjumlahkan besaran tepi Sobel dari setiap piksel yang berkontribusi, sehingga nilai threshold yang lebih besar menuntut tepi yang lebih panjang atau lebih kuat untuk lolos -- yang berarti nilai yang tepat bergantung pada resolusi citra (garis yang lebih panjang pada resolusi lebih tinggi mengumpulkan lebih banyak suara) serta pemandangan, sehingga harus disetel untuk aplikasi tertentu. Sebagai titik awal kasar untuk penyetelan: 1000 untuk garis sederhana dalam citra yang jelas, 500 atau di bawahnya untuk kontras lemah atau garis pendek, 2000 atau lebih untuk pemandangan sibuk di mana garis positif palsu terbentuk melalui kluster noise tepi.
theta_margin dan rho_margin mengontrol penggabungan maksima yang berdekatan. Satu tepi fisik menghasilkan kluster kecil bin bersuara tinggi di sekitar (theta, rho) sebenarnya, dan detektor menyatukan setiap kluster ke puncaknya sebelum dikembalikan. theta_margin=25 (derajat) menggabungkan puncak dalam 25 derajat orientasi; rho_margin=25 (piksel) menggabungkan puncak dalam 25 piksel jarak. Nilai bawaannya masuk akal; menaikkannya mengembalikan garis yang lebih sedikit dan lebih berbeda, sedangkan menurunkannya mengembalikan garis yang lebih banyak dan terkadang terduplikasi.
x_stride dan y_stride melangkah melalui piksel tepi saat voting, dengan cara yang sama seperti mereka melangkah melalui piksel di find_blobs(). Nilai bawaan 2 dan 1 berfungsi untuk kasus umum; menaikkannya mempercepat pencarian dengan mengorbankan resolusi. roi membatasi pencarian ke wilayah bingkai, yang mempersempit garis yang dikembalikan sekaligus mengurangi beban kerja.
Setiap garis yang dikembalikan dapat digambar langsung: objek Line dimasukkan langsung ke draw_line(), yang membaca kolom titik ujung (x1, y1, x2, y2) dari bagian depannya. l.theta adalah sudut dalam derajat, yang mengklasifikasikan garis sebagai horizontal, vertikal, atau diagonal dalam satu perbandingan. l.magnitude adalah total suara, yang mengurutkan garis-garis yang dikembalikan dari yang terkuat ke yang terlemah.
5.26.3. Segmen garis¶
find_lines() adalah detektor yang tepat untuk tepi yang membentang melintasi seluruh bingkai, tetapi banyak tepi nyata -- sisi kiri barcode cetak, tepi atas label, sisi penggaris yang terlihat -- hanya berjalan melintasi sebagian citra. find_line_segments() mengembalikan segmen terbatas yang titik ujungnya berada di dalam bingkai:
segments = img.find_line_segments(merge_distance=5, max_theta_difference=10)
for s in segments:
img.draw_line(s, color=(0, 255, 0))
Detektor segmen menelusuri piksel tepi berorientasi secara langsung, bukan dengan memberikan suara dalam ruang Hough, dan hasilnya adalah kumpulan jalur pendek yang lurus. merge_distance mengatur jarak piksel maksimum yang bisa dijembatani oleh dua jalur pendek yang kolinear dan masih bergabung menjadi satu segmen yang dikembalikan; max_theta_difference mengatur berapa derajat orientasi yang ditoleransi penggabung antara jalur yang berdekatan. Penggabungan yang longgar (merge_distance=10, max_theta_difference=15) menghasilkan jumlah kecil segmen panjang dengan risiko kadang menghubungkan tepi yang benar-benar terpisah; penggabungan yang ketat (merge_distance=0, max_theta_difference=5) menghasilkan banyak segmen pendek dan membiarkan aplikasi memilahnya dalam Python.
Objek hasil adalah tipe Line yang sama seperti yang dikembalikan oleh find_lines(), dengan properti yang sama, sehingga sebuah pipeline dapat memproses kedua jenis deteksi melalui jalur kode hilir yang sama. Satu-satunya perbedaan praktis adalah bahwa titik ujung segmen adalah ujung sebenarnya dari garis dalam citra, sedangkan titik ujung garis tak terbatas adalah di mana pun garis tersebut memotong batas citra.
5.26.4. Kapan menggunakan masing-masing¶
Pilihan antara kedua metode bermuara pada satu pertanyaan: apakah aplikasi peduli di mana garis berhenti?
find_lines() adalah alat yang tepat jika jawabannya tidak. Robot pengikut garis perlu mengetahui ke mana arah garis dan di mana garis itu melintasi bagian bawah bingkai; garis itu sendiri membentang ke cakrawala dan seterusnya. Detektor cakrawala menginginkan tepi berorientasi terkuat dalam citra; tidak perlu mengetahui di mana cakrawala berakhir.
find_line_segments() adalah alat yang tepat jika jawabannya ya. Mengidentifikasi empat sisi persegi panjang cetak membutuhkan empat segmen dengan titik ujung yang diketahui. Melacak jari yang menunjuk ke layar berarti mengikuti segmen pendek yang titik ujungnya adalah ujung dan pangkal jari. Mengukur panjang goresan yang terlihat membutuhkan luasan segmen yang sebenarnya dalam piksel.
Kedua detektor berbagi keterbatasan yang sama: keduanya membutuhkan kontras. Filter tepi Sobel yang menjadi dasarnya merespons gradien kecerahan; tepi berwarna terhadap latar belakang yang sama cerahnya (garis merah di dinding hijau dengan luminansi yang sama) tidak menghasilkan gradien dan tidak menghasilkan deteksi. Ketika kasus seperti itu muncul dalam praktik, solusinya adalah mengekstrak satu saluran LAB sebagai citra skala abu-abu dengan kontras yang tepat sebelum mencari -- to_grayscale() dengan saluran b yang dipilih mengisolasi merah dari hijau di mana saluran luminansi saja tampak datar -- dan menyerahkan citra saluran tersebut ke detektor garis.