5.25. การค้นหาบลอบ

การ Thresholding แปลงเฟรมที่จับภาพได้ให้เป็นมาสก์แบบไบนารี: แต่ละพิกเซลจะผ่านหรือไม่ผ่านการทดสอบค่าขีดแบ่ง ซึ่งตอบคำถามว่า สีใดที่แอปพลิเคชันสนใจปรากฏในฉาก แต่ไม่ตอบว่าอยู่ ที่ไหน -- มาสก์เป็นเพียงทะเลของ 1 และ 0 ขั้นตอนต่อไปคือการตรวจจับบลอบ: การเดินผ่านมาสก์ หาบริเวณที่ต่อเนื่องกันของพิกเซลที่ผ่าน และส่งคืนแต่ละบริเวณเป็นอ็อบเจ็กต์ที่มีตำแหน่ง ขนาด ทิศทาง และคุณสมบัติอื่น ๆ ที่แอปพลิเคชันสามารถนำไปใช้ได้

find_blobs() คือเมธอดหลักสำหรับขั้นตอนนี้ และเป็นจุดเข้าหลักสู่โลกของอ็อบเจ็กต์ผลลัพธ์ในโมดูล image การติดตามลูกบอลสี การตามเส้นที่วาดบนพื้น การนับจุดสว่างที่ thermal sensor เห็น การตัดสินใจว่า LED สีน้ำเงินเปิดหรือปิด -- การเรียกใช้เดียวกันครอบคลุมทั้งหมด อินพุตที่เปลี่ยนไปได้แก่ ค่าขีดแบ่ง บริเวณที่ค้นหา และตัวกรองที่ใช้กับผลลัพธ์ แต่รูปแบบการเรียกยังคงเหมือนเดิม

5.25.1. การเรียกพื้นฐาน

find_blobs รับรายการของค่าขีดแบ่งและส่งคืนรายการของอ็อบเจ็กต์ผลลัพธ์บลอบ:

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))

แต่ละทูเพิลค่าขีดแบ่งมีรูปแบบเดียวกับค่าขีดแบ่งที่ส่งให้ binary() -- หกรายการ (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) สำหรับภาพ RGB565 (ขอบเขตอยู่ใน LAB) และสองรายการ (lo, hi) สำหรับภาพ grayscale สามารถระบุได้ถึง 32 ค่าขีดแบ่งในการเรียกครั้งเดียว ซึ่งทำให้ find_blobs() มีความยืดหยุ่นสูง: บีคอนสีแดง เขียว และน้ำเงินสามารถติดตามพร้อมกันได้ แต่ละสีมีส่วนร่วมกับบลอบของตัวเองในรายการที่ส่งคืน และคุณสมบัติ code ของแต่ละบลอบระบุว่าตรงกับค่าขีดแบ่งใด

การเรียก draw_rectangle() และ draw_cross() ด้านบนใช้ใส่คำอธิบายประกอบบนเฟรมที่จับภาพสำหรับการแสดงผลใน IDE ผลลัพธ์ของบลอบมี b.rect (กรอบล้อมรอบในรูปแบบ 4-ทูเพิล) และ b.cx / b.cy (จุดศูนย์กลางในรูปแบบจำนวนเต็ม) อยู่แล้ว ดังนั้นการวาดการตรวจจับกลับเข้าเฟรมทำได้ด้วยการเรียกเมธอดสองครั้ง

5.25.2. สิ่งที่ผลลัพธ์ประกอบด้วย

แต่ละ Blob คืออาร์เรย์แบบแอตทริบิวต์ที่รวมทุกสิ่งที่ตัวตรวจจับวัดได้เกี่ยวกับบริเวณนั้น คุณสมบัติแบ่งออกเป็นสี่กลุ่ม

กลุ่ม กรอบล้อมรอบและจุดศูนย์กลาง -- x, y, w, h, rect, cx, cy, cxf, cyf -- อธิบายตำแหน่งของบลอบ rect คือ 4-ทูเพิล (x, y, w, h) ที่เมธอดการวาดคาดหวัง cx และ cy คือจุดศูนย์กลางในพิกัดพิกเซลจำนวนเต็ม cxf และ cyf คือจุดศูนย์กลางในพิกัดทศนิยมระดับย่อยพิกเซล มีประโยชน์เมื่อการสอบเทียบต้นน้ำสนใจตำแหน่งเชิงเศษส่วน

กลุ่ม ตัวบ่งชี้รูปร่าง -- pixels, area, density, perimeter, roundness, elongation, compactness, rotation -- อธิบายว่าบลอบมีลักษณะอย่างไร pixels คือจำนวนพิกเซลที่ผ่าน area คือพื้นที่ของกรอบล้อมรอบที่ตรงแกน (w * h) density คืออัตราส่วนของสองค่านี้ ซึ่งเข้าใกล้ 1.0 สำหรับสี่เหลี่ยมผืนผ้าทึบ และลดลงเข้าใกล้ 0.0 สำหรับเส้นทแยงมุมบาง roundness และ compactness ทั้งคู่ให้คะแนนความกลมของบลอบจากมุมมองเรขาคณิตที่แตกต่างกัน (roundness จากโมเมนต์อันดับสอง compactness จากอัตราส่วนเส้นรอบวงต่อพื้นที่) elongation คือ 1.0 - roundness เพื่อความสะดวก rotation คือทิศทางของแกนหลักในหน่วยเรเดียน ซึ่งแม่นยำที่สุดบนบลอบยาวและมีความผิดพลาดบนบลอบที่เกือบกลม (แกนที่คลุมเครือไม่มีทิศทางที่ชัดเจน)

กลุ่ม ค่าขีดแบ่งและข้อมูลเมตาการรวม -- code, count -- ระบุว่าค่าขีดแบ่งใดตรงกันและมีกี่บลอบต้นทางที่ถูกรวมเข้าด้วยกันในผลลัพธ์ code คือบิตแมป 32 บิตที่มีหนึ่งบิตต่อค่าขีดแบ่งที่ตรงกัน (ค่าขีดแบ่งเดียวให้ code == 1 บลอบหลายสีที่รวมกันสามารถมีหลายบิตที่ตั้ง) count คือ 1 เว้นแต่ merge=True จะรวมการตรวจจับหลายรายการเป็นหนึ่ง

กลุ่ม มุม -- corners, min_corners -- ให้รูปทรงเรขาคณิตแบบหมุนของบลอบ corners คือ 4-ทูเพิลของค่าสุดขีด (x, y) ที่นำมาจากคอนทัวร์ของบลอบ เรียงตามเข็มนาฬิกาจากบนซ้าย min_corners คือ 4-ทูเพิลของมุมสำหรับสี่เหลี่ยมผืนผ้าหมุนที่มีพื้นที่น้อยที่สุดซึ่งล้อมรอบบลอบ สี่เหลี่ยมผืนผ้าพื้นที่น้อยที่สุดคือการพอดีแน่น rect ที่ตรงแกนคือการพอดีหลวมที่ตรงกับตารางพิกเซล ทั้งสองมีประโยชน์ขึ้นอยู่กับว่าขั้นตอนปลายน้ำต้องการกรอบที่มีทิศทางหรือกรอบธรรมดา

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.

บลอบมีกรอบล้อมรอบที่ตรงแกน (rect, x, y, w, h) จุดศูนย์กลาง (cx, cy หรือระดับย่อยพิกเซล cxf, cyf) สี่เหลี่ยมผืนผ้าหมุนพื้นที่น้อยที่สุด (min_corners บวกกับ rotation) และเส้นแกนหลัก/แกนรองที่เป็นตัวเลือกซึ่งคำนวณโดยฟังก์ชันระดับโมดูลด้านล่าง

5.25.4. การรวมบลอบที่ทับซ้อนกัน

merge=True ประมวลผลรายการผลลัพธ์ภายหลังเพื่อรวมบลอบที่กรอบล้อมรอบทับซ้อนกัน การใช้งานตามธรรมชาติคือการตรวจจับเป้าหมายที่กล้องเห็นสีเป็นหลายบริเวณที่ถูก threshold เนื่องจากแสงสะท้อน เส้นเงา หรือแสงที่ไม่เท่ากันทั่ววัตถุ: ลูกบอลสีแดงอาจส่งคืนเป็นบลอบสีแดงสามหรือสี่อันขนาดเล็กที่รวมกันติดตามลูกบอล ด้วย merge=True สามบลอบกลายเป็นบลอบใหญ่หนึ่งอัน rect ครอบคลุมการรวม code คือ OR แบบบิตไวส์ของโค้ดของบลอบที่รวมกัน (ดังนั้นการรวมหลายสีระบุว่าสีใดมีส่วนร่วม) และ count รายงานจำนวนบลอบต้นทางที่รวมกัน

margin ขยายหรือลดกรอบล้อมรอบก่อนการทดสอบการทับซ้อน ด้วย margin=2 บลอบที่กรอบล้อมรอบอยู่ห่างกัน 2 พิกเซลก็ยังรวมกัน ด้วย margin=-2 เฉพาะบลอบที่กรอบล้อมรอบทับซ้อนกันอย่างน้อย 2 พิกเซลเท่านั้นที่จะรวมกัน การปรับจูนตามธรรมชาติ: margin บวกเพื่อจัดการบลอบที่ค่าขีดแบ่งแบ่งออกเป็นชิ้นที่อยู่ติดกัน margin ลบเพื่อแยกวัตถุที่แตกต่างกันที่อยู่ใกล้ชิดกัน

merge_cb ทำงานบนแต่ละคู่ที่เป็นตัวเลือกก่อนที่การรวมจะเกิดขึ้น คอลแบ็กรับบลอบสองอันและส่งคืน True เพื่ออนุญาตการรวมหรือ False เพื่อป้องกัน นี่คือเครื่องมือที่เหมาะสำหรับการตรวจสอบการรวมที่กฎเรขาคณิตพลาด -- ตัวอย่างเช่น ปฏิเสธการรวมบลอบสองอันที่มุม rotation ต่างกันมากกว่าค่าขีดแบ่ง หรือปฏิเสธการรวมบลอบขนาดเล็กเข้ากับบลอบขนาดใหญ่กว่ามากหากบลอบขนาดเล็กเป็นแค่สัญญาณรบกวน

5.25.5. ฮิสโตแกรมการฉาย

x_hist_bins_max และ y_hist_bins_max แนบ ฮิสโตแกรมการฉาย ที่เป็นตัวเลือกกับแต่ละบลอบ ฮิสโตแกรมการฉายคือการนับพิกเซลที่ผ่านตามแกนหนึ่ง: ฮิสโตแกรมแกน X รวมพิกเซลที่ผ่านต่อคอลัมน์ภายในกรอบล้อมรอบของบลอบ และฮิสโตแกรมแกน Y รวมต่อแถว ทั้งคู่มีค่าเริ่มต้นเป็นศูนย์ -- ฮิสโตแกรมจะไม่ถูกคำนวณเว้นแต่จะระบุ max ที่ไม่ใช่ศูนย์ เนื่องจากมิฉะนั้นจะเพิ่มงานให้กับทุกการตรวจจับ

เมื่อคำนวณแล้ว ฮิสโตแกรมให้สัญญาณ 1 มิติที่ราคาถูกซึ่งแอปพลิเคชันสามารถวิเคราะห์เพิ่มเติม: ตรวจจับตำแหน่งของแถบแนวตั้งภายในบลอบ หาจุดเปลี่ยนของเป้าหมายสองสี นับจำนวนช่องว่างตามแกนยาว พวกมันถูกเก็บเป็นคุณสมบัติ x_hist_bins และ y_hist_bins บนแต่ละ Blob

5.25.6. ฟังก์ชันช่วยเหลือเรขาคณิตเพิ่มเติม

ค่าการวัดเรขาคณิตเพิ่มเติมอีกหลายค่าอยู่เป็นฟังก์ชันระดับโมดูลที่รับบลอบและส่งคืนการวัดที่ต้องการ:

  • image.get_solidity() ส่งคืน solidity ของบลอบ -- จำนวนพิกเซลหารด้วยพื้นที่ของ convex hull บริเวณทึบที่เต็มจะใกล้ 1.0 บลอบที่มีรอยเว้า (รูปเกือกม้า มือที่กางนิ้ว) จะต่ำกว่ามาก

  • image.get_convexity() ส่งคืน convexity -- เส้นรอบวงของ convex hull หารด้วยเส้นรอบวงของบลอบ บลอบที่นูนสมบูรณ์คือ 1.0 บลอบที่หยักหรือมีรอยบากจะต่ำกว่า

  • image.get_major_axis_line() และ image.get_minor_axis_line() ส่งคืนอ็อบเจ็กต์ Line ตามแกนหลักและแกนรองของบลอบ ซึ่งได้มาจากสี่เหลี่ยมผืนผ้าหมุนพื้นที่น้อยที่สุด

  • image.get_enclosing_circle() ส่งคืน Circle ที่ล้อมรอบบลอบ -- มีประโยชน์เมื่อขั้นตอนปลายน้ำต้องการวงกลมเพื่อวาดหรือทดสอบ

  • image.get_enclosed_ellipse() ส่งคืน 5-ทูเพิล (cx, cy, rx, ry, rotation) สำหรับรูปวงรีที่ถูกล้อมในสี่เหลี่ยมผืนผ้าหมุนพื้นที่น้อยที่สุดของบลอบ ค่าเหล่านี้ป้อนโดยตรงเข้า draw_ellipse()

5.25.7. การเรียนรู้ค่าขีดแบ่งอัตโนมัติ

ตัวตรวจจับบลอบดีได้เท่าค่าขีดแบ่งที่ใช้งาน และการ หา ค่าขีดแบ่งที่ถูกต้องสำหรับสีเป้าหมายเป็นปัญหาของตัวเอง รูปแบบทั่วไปสองแบบช่วยลดงานนั้น

แบบแรกคือ การเลือกแบบโต้ตอบใน IDE: จับภาพเฟรม ลากสี่เหลี่ยมรอบตัวอย่างของสีเป้าหมาย และให้ ตัวแก้ไขค่าขีดแบ่ง ของ IDE รายงานขอบเขต LAB ที่เห็น ขอบเขตเหล่านั้นถูกนำไปใส่ในสคริปต์เป็นค่าขีดแบ่งของ find_blobs() และตัวตรวจจับก็พร้อมใช้งาน

แบบที่สองคือการเรียนรู้อัตโนมัติแบบโปรแกรม: รูทีนการสอบเทียบที่ทำงานบนกล้องจับภาพเฟรม นำฮิสโตแกรมของแพตช์ที่รู้จักซึ่งเป้าหมายอยู่ (get_histogram() พร้อม roi=) และอ่านช่วงค่าของแพตช์จากฮิสโตแกรมด้วย get_percentile() เปอร์เซ็นไทล์ที่ 5 กำหนดขอบเขตต่ำของแต่ละช่อง และที่ 95 กำหนดขอบเขตสูง โดยละเว้นพิกเซลที่ผิดปกติที่ทั้งสองปลาย บนภาพ RGB565 การเรียก percentile หนึ่งครั้งรายงานช่อง LAB ทั้งสามพร้อมกัน ดังนั้นการเรียกสองครั้งให้ตัวเลขหกค่าที่ 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)