5.27. Menemukan lingkaran dan persegi panjang

Garis dan segmen mencakup tepi lurus dalam bingkai yang ditangkap, tetapi banyak fitur dunia nyata yang dicari kamera tidaklah lurus. Koin yang terletak di meja adalah sebuah lingkaran. Label cetak, catatan tempel, bagian atas kotak yang dilihat dari sudut miring adalah segi empat. Modul image menyediakan detektor khusus untuk masing-masing: pencarian berbasis Hough untuk lingkaran dan pencarian berbasis AprilTag untuk bentuk empat sisi.

Kedua metode mengikuti templat yang sama seperti detektor garis -- threshold mengontrol berapa banyak suara yang dibutuhkan sebuah deteksi, roi mempersempit pencarian, dan objek yang dikembalikan membawa posisi sekaligus besaran kepercayaan -- tetapi ruang parameter dan nilai bawaan yang tepat cukup berbeda sehingga perlu dijelaskan secara khusus.

5.27.1. Lingkaran Hough

find_circles() menjalankan varian lingkaran dari transformasi Hough. Setiap piksel tepi dari pra-proses Sobel memberikan suara untuk setiap lingkaran yang bisa melewatinya; lingkaran yang mengumpulkan cukup banyak suara akan dikembalikan.

circles = img.find_circles(threshold=3500,
                            x_margin=10, y_margin=10, r_margin=10,
                            r_min=10, r_max=80, r_step=2)

for c in circles:
    img.draw_circle((c.x, c.y, c.r), color=(255, 0, 0))

threshold adalah jumlah minimum besaran tepi Sobel sepanjang lingkaran kandidat. Lingkaran yang lebih besar melacak lebih banyak piksel sehingga membutuhkan ambang batas yang lebih tinggi untuk lolos; nilai yang menemukan koin berradius 20 piksel juga akan aktif pada noise di sekitar tepi 100 piksel, sementara nilai yang disetel untuk koin besar akan melewatkan koin kecil. Jika rentang radius target diketahui, nilai ambang batas yang tepat berskala dengan keliling -- kira-kira threshold = 50 * 2 * pi * r memberikan titik awal yang masuk akal dan nilai yang tepat diperoleh dari penyetelan singkat.

r_min, r_max, dan r_step mengatur pencarian radius. Tanpa batasan, detektor akan mencari setiap radius dari beberapa piksel hingga setengah lebar citra, yang mana lambat sekaligus rawan terhadap positif palsu. Menetapkan r_min dan r_max untuk menggepengkan ukuran target yang diharapkan dengan margin yang cukup (misalnya r_min=15, r_max=25 untuk koin yang diketahui berukuran sekitar 20 piksel) mengurangi pekerjaan secara signifikan dan meningkatkan rasio sinyal-ke-noise dari suara. r_step mengontrol granularitas pencarian; langkah yang lebih besar berjalan lebih cepat dan mungkin melewatkan lingkaran yang radius sebenarnya jatuh di antara dua nilai yang disampling. Nilai bawaan r_step=2 adalah kompromi yang wajar.

x_margin, y_margin, dan r_margin mengontrol penggabungan deteksi yang berdekatan, seperti yang dilakukan theta_margin dan rho_margin untuk deteksi garis. Satu lingkaran fisik dalam citra memberikan suara untuk sekumpulan lingkaran kandidat yang pusat dan radiusnya berbeda dalam beberapa piksel; margin-margin tersebut menyatukan setiap kluster ke puncaknya sebelum daftar hasil dibuat. Margin yang lebih besar menghasilkan lebih sedikit deteksi yang lebih terpercaya; margin yang lebih kecil menghasilkan lebih banyak deteksi dengan kemungkinan duplikat.

x_stride dan y_stride melangkah melalui scan voting, dengan cara yang sama seperti pada detektor lainnya. Nilai bawaan 2 dan 1 cocok untuk sebagian besar citra; menaikkan keduanya ke 4 adalah pertukaran kecepatan standar untuk citra yang diketahui mengandung lingkaran besar.

Setiap Circle yang dikembalikan membawa x, y, r (pusat dan radius) serta magnitude (total suara, berguna sebagai skor kepercayaan untuk pengurutan atau penyaringan). Menggambar deteksi kembali ke dalam bingkai hanya membutuhkan satu panggilan -- draw_circle() menerima tuple 3 (x, y, r), yang tersedia sebagai (c.x, c.y, c.r) langsung dari hasil.

5.27.2. Persegi Panjang

find_rects() meminjam detektor segi empat dari pipeline AprilTag -- rutinitas yang sama yang menemukan kotak hitam di sekitar tag diekspos secara mandiri sebagai pencari persegi panjang serbaguna.

rects = img.find_rects(threshold=12000)

for r in rects:
    img.draw_rectangle(r.rect, color=(0, 255, 0))
    for corner in r.corners:
        img.draw_circle((corner[0], corner[1], 4),
                        color=(0, 255, 0))

threshold adalah jumlah minimum besaran tepi di sekeliling perimeter persegi panjang. Persegi panjang hitam-di-atas-putih yang dicetak dalam bingkai yang terang dengan mudah melampaui 10000; persegi panjang yang samar pada latar bertekstur mungkin perlu diturunkan ke 2000 -- menukar positif palsu dengan sensitivitas. Seperti detektor lingkaran, nilai yang tepat diperoleh dari penyetelan cepat dengan target yang dimaksud dalam pandangan.

Detektor ini bersifat proyektif -- ia menemukan segi empat yang sisinya lurus tetapi tidak harus sejajar atau sejajar sumbu. Label yang dilihat dari sudut miring tampak seperti trapesium dalam citra, dan detektor persegi panjang menemukannya dengan benar; persegi panjang sejajar-sumbu hanyalah kasus degenerat di mana empat sudutnya kebetulan membentuk kotak bersudut siku-siku. roi membatasi pencarian; argumen kata kunci lainnya mengambil nilai bawaannya dari pipeline AprilTag dan jarang perlu disetel.

Setiap Rect yang dikembalikan membawa kotak pembatas sejajar-sumbu -- x, y, w, h, ditambah tuple 4 rect yang diharapkan oleh draw_rectangle() -- dan empat sudut yang terdeteksi sebagai corners. Kotak pembatas digunakan oleh aplikasi untuk posisi dan ukuran perkiraan; sudut-sudut tersebut mendeskripsikan segi empat proyektif itu sendiri. Ketika kamera melihat target datar dari sudut dan aplikasi perlu membatalkan efek keystone -- membaca teks pada label, mengambil sampel warna dari bidang datar -- sudut-sudut tersebut dimasukkan langsung ke rotation_corr() dengan kata kunci corners= (lihat koreksi lensa dan perspektif), dan hasilnya adalah persegi panjang yang telah diluruskan dan siap untuk analisis apa pun berikutnya.

Peringatan

Karena detektor disetel untuk kebutuhan pipeline AprilTag -- segi empat dengan tepi yang kuat dan kontras tinggi, seperti garis tepi tag hitam di atas kertas putih -- ini bukan pass temukan-semua-persegi-panjang. Persegi panjang dengan kontras rendah, tepi bertekstur, atau lingkungan yang sibuk bisa luput sama sekali. Seberapa baik hasilnya tergantung situasi: uji terhadap target nyata sejak awal, sebelum membangun pipeline di atasnya.

5.27.3. Saat detektor salah bekerja

Lingkaran khususnya mendapat manfaat dari pra-filter pada input. Bingkai yang berisik menghasilkan banyak piksel tepi liar yang semuanya memberikan suara, dan ruang Hough yang dihasilkan memiliki puncak-puncak yang lebar dan tidak jelas yang sulit dipisahkan oleh penggabung. Sebuah pass gaussian() atau mean() sebelum find_circles() memperhalus noise dan menyisakan tepi yang sebenarnya; detektor mengembalikan puncak yang lebih bersih dalam waktu yang lebih singkat.

Untuk persegi panjang, mode kegagalan yang umum adalah kebalikannya: kontras rendah antara persegi panjang dan latar belakangnya berarti jumlah besaran tepi tidak pernah melampaui threshold. Sebuah pass histeq() untuk mendistribusikan ulang rentang kecerahan ke seluruh rentang 0-hingga-255 memulihkan kontras yang dibutuhkan detektor. (Kontras harus ada di suatu tempat dalam citra; ekualisasi histogram hanya dapat memperkuat apa yang sudah ada.)