5.25. Menemukan blob¶
Thresholding mengubah bingkai yang ditangkap menjadi mask biner: setiap piksel lolos uji ambang batas atau tidak. Ini menjawab warna mana yang dipedulikan aplikasi yang muncul di scene, tetapi tidak menjawab di mana -- mask tersebut hanyalah lautan angka 1 dan 0. Langkah berikutnya adalah deteksi blob: menelusuri mask, menemukan wilayah yang saling terhubung dari piksel yang lolos, dan mengembalikan masing-masing sebagai objek dengan posisi, ukuran, orientasi, dan properti lain yang dapat digunakan oleh aplikasi.
find_blobs() adalah metode utama untuk langkah tersebut, dan merupakan titik masuk paling umum ke dalam dunia result-object modul image. Melacak bola berwarna, mengikuti garis yang dilukis di lantai, menghitung berapa banyak titik terang yang dilihat sensor termal, memutuskan apakah LED biru menyala atau mati -- panggilan yang sama mencakup semuanya. Input yang berubah (ambang batas, wilayah yang dicari, filter yang diterapkan pada hasil), tetapi pola panggilan tetap sama.
5.25.1. Panggilan dasar¶
find_blobs menerima daftar ambang batas dan mengembalikan daftar objek hasil blob:
thresholds = [(30, 100, 15, 127, 15, 127)] # LAB threshold for red
blobs = img.find_blobs(thresholds)
for b in blobs:
img.draw_rectangle(b.rect, color=(255, 0, 0))
img.draw_cross(b.cx, b.cy, color=(255, 0, 0))
Setiap tuple ambang batas memiliki bentuk yang sama dengan ambang batas yang diteruskan ke binary() -- enam entri (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) untuk citra RGB565 (batasnya dalam LAB), dua entri (lo, hi) untuk citra skala abu-abu. Hingga 32 ambang batas dapat diberikan dalam satu panggilan, itulah yang membuat find_blobs() sangat fleksibel: beacon merah, hijau, dan biru dapat dilacak secara bersamaan, masing-masing berkontribusi blob-nya sendiri ke daftar yang dikembalikan, dan properti code setiap blob mengidentifikasi ambang batas mana yang cocok.
Panggilan draw_rectangle() dan draw_cross() di atas memberi anotasi pada bingkai yang ditangkap untuk pratinjau IDE. Hasil blob sudah memuat b.rect (kotak pembatas sebagai 4-tuple) dan b.cx / b.cy (sentroid integer), sehingga menggambar deteksi kembali ke bingkai hanya membutuhkan dua panggilan metode.
5.25.2. Apa yang dimuat dalam hasil¶
Setiap Blob adalah attribute-tuple yang mengemas semua yang diukur detektor tentang wilayah tersebut. Properti dibagi menjadi empat kelompok.
Kelompok kotak pembatas dan sentroid -- x, y, w, h, rect, cx, cy, cxf, cyf -- mendeskripsikan posisi blob. rect adalah 4-tuple (x, y, w, h) yang diharapkan oleh metode-metode gambar; cx dan cy adalah sentroid dalam koordinat piksel integer; cxf dan cyf adalah sentroid dalam koordinat float sub-piksel, berguna ketika kalibrasi upstream peduli terhadap posisi pecahan.
Deskriptor bentuk* -- pixels, area, density, perimeter, roundness, elongation, compactness, rotation -- mendeskripsikan tampilan blob. pixels adalah jumlah piksel yang lolos; area adalah luas kotak pembatas sejajar sumbu (w * h); density adalah rasio keduanya, yang mendekati 1.0 untuk persegi panjang padat dan mendekati 0.0 untuk goresan diagonal tipis. roundness dan compactness keduanya menilai seberapa bulat blob tersebut, dari sudut pandang geometri yang berbeda (roundness dari momen orde kedua, compactness dari rasio keliling-ke-luas); elongation adalah 1.0 - roundness untuk kemudahan. rotation adalah orientasi sumbu mayor dalam radian, yang paling akurat pada blob memanjang dan menjadi berisik pada yang hampir bulat (sumbu yang ambigu tidak memiliki arah yang terdefinisi dengan baik).
Metadata ambang batas dan penggabungan* -- code, count -- mengidentifikasi ambang batas mana yang cocok dan berapa banyak blob sumber yang digabungkan ke dalam yang dikembalikan. code adalah bitmap 32-bit dengan satu bit yang diset per ambang batas yang cocok (satu ambang batas memberikan code == 1; blob multi-warna yang digabung dapat memiliki beberapa bit yang diset); count adalah 1 kecuali merge=True menggabungkan beberapa deteksi menjadi satu.
Kelompok sudut -- corners, min_corners -- memberikan geometri rotasi blob. corners adalah 4-tuple dari ekstrem (x, y) yang diambil dari kontur blob, diurutkan searah jarum jam dari kiri atas; min_corners adalah 4-tuple sudut untuk persegi panjang berputar dengan luas minimum yang membungkus blob. Persegi panjang luas minimum adalah kesesuaian ketat; rect sejajar sumbu adalah kesesuaian longgar yang disejajarkan dengan kisi piksel. Keduanya berguna tergantung apakah tahap hilir membutuhkan kotak berorientasi atau kotak biasa.
Sebuah blob membawa kotak pembatas sejajar sumbu (rect, x, y, w, h), sentroid (cx, cy atau sub-piksel cxf, cyf), persegi panjang berputar luas minimum (min_corners ditambah rotation), dan garis sumbu mayor / minor opsional yang dihitung oleh fungsi pembantu tingkat modul di bawah ini.¶
5.25.3. Memfilter pencarian¶
Bingkai yang ditangkap biasanya mengandung piksel yang cocok dengan ambang batas karena alasan selain objek yang dipedulikan aplikasi: sorotan spekuler, objek latar belakang jauh, piksel noise citra yang kebetulan jatuh dalam rentang LAB. Argumen kata kunci ke find_blobs() adalah lini pertahanan pertama.
roi membatasi pencarian ke suatu wilayah bingkai, seperti yang dilakukan setiap metode modul image lainnya. Aplikasi yang mengetahui objek hanya dapat muncul di separuh bawah bidang pandang meneruskan roi=(0, h//2, w, h//2) dan mengabaikan semua yang ada di atasnya; waktu yang tersimpan dikembalikan ke frame rate.
area_threshold dan pixels_threshold keduanya memfilter blob yang terlalu kecil untuk diperhatikan. area_threshold membuang blob yang kotak pembatasnya memiliki piksel area lebih sedikit dari nilai tersebut (baik untuk memfilter noise yang tersebar); pixels_threshold membuang blob yang memiliki piksel lolos lebih sedikit dari nilai tersebut (baik untuk memfilter blob yang besar tetapi jarang, seperti pola stippling yang di-threshold dengan satu atau dua piksel yang cocok di sana-sini). Kedua nilai default adalah 10; meningkatkannya ke ratusan untuk target latar depan beberapa sentimeter membuang setiap bercak noise kecil.
x_stride dan y_stride mengatur langkah piksel yang diambil pemindai saat mencari blob untuk mulai melacak. Stride bukan resolusi pelacakan -- pelacakan selalu mengikuti batas blob sebenarnya dengan detail piksel tunggal -- tetapi mengontrol seberapa cepat pemindaian menemukan piksel awal. Ketika blob diketahui besar (target berwarna seukuran kepalan tangan sejarak kaki dari kamera, mudah ratusan piksel), x_stride=4, y_stride=4 memotong waktu pemindaian sebanyak enam belas kali tanpa kehilangan praktis dalam deteksi. Ketika blob kecil (beacon LED yang jauh, beberapa piksel), stride harus tetap di 1 untuk menghindari melangkahi seluruhnya. invert membalik uji ambang batas: yang cocok menjadi tidak cocok dan rutinitas mengembalikan blob dari piksel yang gagal.
threshold_cb adalah callback Python yang dipanggil pada setiap blob setelah thresholding tetapi sebelum daftar hasil akhir dibuat. Callback menerima blob dan mengembalikan True untuk menyimpannya atau False untuk membuangnya. Ini adalah tempat untuk menerapkan filter level Python yang sewenang-wenang pada properti yang tidak diekspos langsung oleh argumen kata kunci -- density minimum, rentang rotasi tertentu, kombinasi bit-code kustom setelah penggabungan. Argumen kata kunci adalah filter dalam kode native dan berjalan cepat; callback berjalan dalam Python dan lebih lambat tetapi tidak terbatas dalam apa yang dapat diekspresikannya.
5.25.4. Menggabungkan blob yang tumpang tindih¶
merge=True memproses pasca daftar hasil untuk menggabungkan blob yang persegi panjang kotak pembatasnya tumpang tindih. Penggunaan alami adalah mendeteksi target yang warnanya dilihat kamera sebagai beberapa wilayah yang di-threshold karena sorotan spekuler, garis bayangan, atau pencahayaan yang tidak cocok di seluruh objek: satu bola merah mungkin kembali sebagai tiga atau empat blob merah kecil yang, secara bersama-sama, menelusuri bola tersebut. Dengan merge=True, tiga blob menjadi satu blob besar, rect menutupi gabungan, code adalah OR bitwise dari kode blob yang digabung (sehingga penggabungan multi-warna mengidentifikasi warna mana yang berkontribusi), dan count melaporkan berapa banyak blob sumber yang digabungkan.
margin memperbesar atau memperkecil kotak pembatas sebelum uji tumpang tindih. Dengan margin=2, blob yang kotak pembatasnya berada dalam 2 piksel satu sama lain masih bergabung; dengan margin=-2, hanya blob yang kotak pembatasnya tumpang tindih setidaknya 2 piksel yang bergabung. Penyetelan alami: margin positif untuk menangani blob yang dipecah oleh ambang batas menjadi bagian yang berdekatan; margin negatif untuk memisahkan objek yang berbeda yang berkelompok rapat.
merge_cb berjalan pada setiap pasangan kandidat sebelum penggabungan terjadi. Callback menerima dua blob dan mengembalikan True untuk mengizinkan penggabungan atau False untuk mencegahnya. Ini adalah alat yang tepat untuk memeriksa penggabungan silang yang dilewatkan aturan geometri -- misalnya, menolak menggabungkan dua blob yang sudut rotation-nya berbeda lebih dari satu ambang batas, atau menolak menggabungkan blob kecil ke blob yang jauh lebih besar jika blob kecilnya hanyalah bercak.
5.25.5. Histogram proyeksi¶
x_hist_bins_max dan y_hist_bins_max melampirkan histogram proyeksi opsional ke setiap blob. Histogram proyeksi adalah jumlah piksel yang lolos sepanjang satu sumbu: histogram sumbu-X menjumlahkan piksel yang lolos per kolom di dalam kotak pembatas blob, dan histogram sumbu-Y menjumlahkan per baris. Keduanya default ke nol -- histogram tidak dihitung kecuali max non-nol diberikan, karena jika tidak akan menambahkan pekerjaan pada setiap deteksi.
Ketika dihitung, histogram menyediakan sinyal 1-D murah yang dapat dijalankan lebih lanjut oleh aplikasi: mendeteksi posisi garis vertikal di dalam blob, menemukan titik pemisah target dua warna, menghitung berapa banyak celah yang muncul sepanjang sumbu panjang. Mereka diisi sebagai properti x_hist_bins dan y_hist_bins pada setiap Blob.
5.25.6. Pembantu geometri tambahan¶
Sejumlah ukuran geometri lebih lanjut berada sebagai fungsi tingkat modul yang menerima blob dan mengembalikan pengukuran yang diminta:
image.get_solidity()mengembalikan soliditas blob -- piksel dibagi dengan luas convex hull. Wilayah yang terisi padat mendekati1.0; blob dengan cekungan (ladam kuda, tangan dengan jari terentang) turun jauh di bawahnya.image.get_convexity()mengembalikan konveksitas -- keliling convex hull dibagi dengan keliling blob. Blob yang benar-benar cembung adalah1.0; blob bergerigi atau bertakik lebih rendah.image.get_major_axis_line()danimage.get_minor_axis_line()mengembalikan objekLinesepanjang sumbu mayor dan minor blob, yang diturunkan dari persegi panjang berputar luas minimum.image.get_enclosing_circle()mengembalikanCircleyang membungkus blob -- berguna ketika tahap hilir menginginkan lingkaran untuk digambar atau diuji.image.get_enclosed_ellipse()mengembalikan 5-tuple(cx, cy, rx, ry, rotation)untuk elips yang diinscribed dalam persegi panjang luas minimum blob. Nilai-nilai tersebut langsung digunakan kedraw_ellipse().
5.25.7. Mempelajari ambang batas secara otomatis¶
Detektor blob hanya sebaik ambang batas yang digunakan, dan pekerjaan menemukan ambang batas yang tepat untuk warna target adalah masalah tersendiri. Dua pola umum mengurangi pekerjaan tersebut.
Yang pertama adalah pemilihan interaktif di IDE: tangkap bingkai, seret persegi panjang di sekitar contoh warna target, dan biarkan editor ambang batas IDE melaporkan batas LAB yang dilihatnya. Batas-batas tersebut dimasukkan ke dalam skrip sebagai ambang batas find_blobs() dan detektor siap digunakan.
Yang kedua adalah auto-learn terprogram: rutinitas kalibrasi yang berjalan di kamera menangkap bingkai, mengambil histogram dari patch yang diketahui di mana target berada (get_histogram() dengan roi=), dan membaca rentang nilai patch dari histogram dengan get_percentile(). Persentil ke-5 menetapkan batas rendah setiap saluran dan yang ke-95 batas tingginya, mengabaikan piksel outlier yang menyimpang di kedua ujung. Pada citra RGB565 satu panggilan persentil melaporkan ketiga saluran LAB sekaligus, sehingga dua panggilan menghasilkan enam angka yang diharapkan oleh find_blobs():
h = img.get_histogram(roi=patch)
lo = h.get_percentile(0.05)
hi = h.get_percentile(0.95)
threshold = (lo.l_value, hi.l_value,
lo.a_value, hi.a_value,
lo.b_value, hi.b_value)