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.

A blob detection illustrated against a binary threshold mask. The left panel shows a tilted oval mask of passing pixels. The right panel shows the same mask annotated with the axis-aligned bounding box drawn around it, the centroid marked with a cross in the middle, a dashed minimum-area rotated rectangle hugging the oval at its true angle, and the major-axis line through the centroid pointing along the oval's long direction.

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.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 mendekati 1.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 adalah 1.0; blob bergerigi atau bertakik lebih rendah.

  • image.get_major_axis_line() dan image.get_minor_axis_line() mengembalikan objek Line sepanjang sumbu mayor dan minor blob, yang diturunkan dari persegi panjang berputar luas minimum.

  • image.get_enclosing_circle() mengembalikan Circle yang 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 ke draw_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)