5.27. การค้นหาวงกลมและสี่เหลี่ยม

เส้นและส่วนของเส้นครอบคลุมขอบ ตรง ในเฟรมที่ถ่ายภาพมา แต่ลักษณะเด่นในโลกจริงจำนวนมากที่กล้องค้นหานั้นไม่ตรง เหรียญที่วางบนโต๊ะคือ วงกลม ป้ายพิมพ์ กระดาษโน้ต หรือฝากล่องที่มองจากมุมเฉียงคือ สี่เหลี่ยมด้านขนาน โมดูล image นำเสนอตัวตรวจจับเฉพาะทางสำหรับแต่ละรูปแบบ ได้แก่ การค้นหาวงกลมแบบ Hough และการค้นหาทรงสี่ด้านที่ดัดแปลงมาจาก AprilTag

ทั้งสองวิธีใช้รูปแบบเดียวกับตัวตรวจจับเส้น -- threshold ควบคุมจำนวนโหวตขั้นต่ำที่การตรวจจับต้องได้รับ roi จำกัดขอบเขตการค้นหา และออบเจกต์ที่คืนค่ามีทั้งตำแหน่งและค่าความเชื่อมั่น -- แต่ช่วงพารามิเตอร์และค่าเริ่มต้นที่เหมาะสมแตกต่างกันพอที่จะต้องอธิบายแยกกัน

5.27.1. วงกลม Hough

find_circles() รัน Hough transform แบบวงกลม พิกเซลขอบแต่ละพิกเซลจากขั้นตอนก่อนประมวลผล Sobel โหวตให้กับทุกวงกลมที่อาจผ่านพิกเซลนั้น วงกลมที่ได้รับโหวตเพียงพอจะถูกคืนค่ากลับมา

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 คือผลรวมขั้นต่ำของขนาดขอบ Sobel ตามแนววงกลมที่เป็นตัวเลือก วงกลมที่ใหญ่กว่าจะวาดผ่านพิกเซลมากกว่าจึงต้องการค่า threshold สูงกว่าเพื่อผ่าน ค่าที่ค้นหาเหรียญรัศมี 20 พิกเซลได้จะยังทำงานกับสัญญาณรบกวนรอบขอบขนาด 100 พิกเซลด้วย ขณะที่ค่าที่ปรับสำหรับเหรียญใหญ่จะพลาดเหรียญเล็ก เมื่อทราบช่วงรัศมีเป้าหมาย ค่า threshold ที่เหมาะสมจะสัมพันธ์กับเส้นรอบวง -- ประมาณ threshold = 50 * 2 * pi * r เป็นจุดเริ่มต้นที่สมเหตุสมผลและค่าที่ถูกต้องจะได้จากการปรับค่าสั้นๆ

r_min, r_max และ r_step กำหนด ช่วงการค้นหารัศมี หากไม่มีขอบเขต ตัวตรวจจับจะค้นหาทุกรัศมีตั้งแต่พิกเซลเล็กๆ ไปจนถึงครึ่งหนึ่งของความกว้างภาพ ซึ่งทั้งช้าและเพิ่มโอกาสเกิด false positive การกำหนด r_min และ r_max เพื่อครอบคลุมขนาดเป้าหมายที่คาดไว้ด้วยระยะห่างพอสมควร (เช่น r_min=15, r_max=25 สำหรับเหรียญที่มีรัศมีประมาณ 20 พิกเซล) จะช่วยลดการคำนวณได้มากและปรับปรุงอัตราส่วนสัญญาณต่อสัญญาณรบกวนของโหวต r_step ควบคุมความละเอียดของการค้นหา ค่าที่มากขึ้นทำงานเร็วขึ้นแต่อาจพลาดวงกลมที่รัศมีจริงอยู่ระหว่างสองค่าที่สุ่ม ค่าเริ่มต้น r_step=2 เป็นการประนีประนอมที่สมเหตุสมผล

x_margin, y_margin และ r_margin ควบคุม การรวม การตรวจจับที่อยู่ใกล้กัน เช่นเดียวกับที่ theta_margin และ rho_margin ทำสำหรับการตรวจจับเส้น วงกลมจริงหนึ่งวงในภาพจะโหวตให้กลุ่มวงกลมตัวเลือกที่มีจุดศูนย์กลางและรัศมีตรงกันภายในไม่กี่พิกเซล ค่า margin จะยุบแต่ละกลุ่มให้เหลือค่าสูงสุดก่อนสร้างรายการผลลัพธ์ ค่า margin ที่มากขึ้นคืนการตรวจจับที่น้อยลงแต่น่าเชื่อถือมากขึ้น ค่า margin ที่น้อยลงคืนการตรวจจับมากขึ้นพร้อมกับอาจมีการซ้ำกัน

x_stride และ y_stride กำหนดขั้นตอนการสแกนโหวต เช่นเดียวกับในตัวตรวจจับอื่นๆ ค่าเริ่มต้น 2 และ 1 เหมาะกับภาพส่วนใหญ่ การเพิ่มทั้งคู่เป็น 4 เป็นการแลกเปลี่ยนความเร็วมาตรฐานสำหรับภาพที่ทราบว่ามีวงกลมขนาดใหญ่

แต่ละ Circle ที่คืนค่ามาจะมี x, y, r (จุดศูนย์กลางและรัศมี) และ magnitude (ผลรวมโหวต ซึ่งมีประโยชน์เป็นคะแนนความเชื่อมั่นสำหรับการเรียงลำดับหรือกรอง) การวาดการตรวจจับกลับลงในเฟรมต้องเรียกฟังก์ชันเพียงครั้งเดียว -- draw_circle() รับ 3-tuple (x, y, r) ซึ่งเข้าถึงได้โดยตรงจากผลลัพธ์เป็น (c.x, c.y, c.r)

5.27.2. สี่เหลี่ยม

find_rects() ยืมตัวตรวจจับสี่เหลี่ยมจาก AprilTag pipeline -- ขั้นตอนเดิมที่ระบุตำแหน่งกรอบสี่เหลี่ยมสีดำรอบ tag ถูกเปิดเผยเพียงลำพังเป็นตัวค้นหาสี่เหลี่ยมทั่วไป

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 คือผลรวมขั้นต่ำของขนาดขอบรอบเส้นรอบรูปของสี่เหลี่ยม สี่เหลี่ยมดำบนขาวที่พิมพ์ในเฟรมที่แสงดีจะผ่าน 10000 ได้ง่าย สี่เหลี่ยมที่จางบนพื้นหลังที่มีพื้นผิวอาจต้องลดลงเหลือ 2000 -- เพื่อแลกระหว่าง false positive กับความไว เช่นเดียวกับตัวตรวจจับวงกลม ค่าที่ถูกต้องได้จากการปรับค่าอย่างรวดเร็วกับเป้าหมายที่ต้องการ

ตัวตรวจจับนี้ เป็นแบบ projective -- ค้นหาสี่เหลี่ยมที่ด้านตรงแต่ไม่จำเป็นต้องขนานหรือตรงแนวแกน ป้ายที่มองจากมุมเฉียงจะดูเหมือนสี่เหลี่ยมคางหมูในภาพ และตัวตรวจจับสี่เหลี่ยมค้นหาได้อย่างถูกต้อง สี่เหลี่ยมตรงแนวแกนเป็นเพียงกรณีพิเศษที่มุมทั้งสี่ก่อเป็นกล่องมุมฉาก roi จำกัดการค้นหา อาร์กิวเมนต์คีย์เวิร์ดที่เหลือใช้ค่าเริ่มต้นจาก AprilTag pipeline และแทบไม่ต้องปรับ

แต่ละ Rect ที่คืนค่ามาจะมีกรอบล้อมรอบที่ตรงแนวแกน -- x, y, w, h พร้อม 4-tuple rect ที่ draw_rectangle() ต้องการ -- และ มุมทั้งสี่ที่ตรวจจับได้ในรูป corners กรอบล้อมรอบคือสิ่งที่แอปพลิเคชันใช้สำหรับตำแหน่งและขนาดคร่าวๆ ส่วน corners อธิบายสี่เหลี่ยม projective จริงๆ เมื่อกล้องมองเป้าหมายแบน 2 มิติจากมุมหนึ่งและแอปพลิเคชันต้องการยกเลิกการบิดเบี้ยว -- อ่านข้อความบนป้าย สุ่มตัวอย่างสีจากพื้นที่แบน -- corners จะป้อนโดยตรงเข้า rotation_corr() ด้วยคีย์เวิร์ด corners= (ดู การแก้ไขเลนส์และมุมมอง) และผลลัพธ์คือสี่เหลี่ยมที่แก้ไขแล้วพร้อมสำหรับการวิเคราะห์ต่อไป

Warning

เนื่องจากตัวตรวจจับได้รับการปรับแต่งสำหรับสิ่งที่ AprilTag pipeline ต้องการ -- สี่เหลี่ยมที่มีขอบเข้ม ความเปรียบต่างสูง เช่น กรอบ tag สีดำบนกระดาษขาว -- มันไม่ใช่การค้นหาสี่เหลี่ยมทุกรูปแบบ สี่เหลี่ยมที่มีความเปรียบต่างอ่อน ขอบที่มีพื้นผิว หรือพื้นหลังที่วุ่นวายอาจไม่ถูกตรวจจับเลย ประสิทธิภาพขึ้นอยู่กับสถานการณ์ ทดสอบกับเป้าหมายจริงตั้งแต่ต้น ก่อนสร้าง pipeline รอบมัน

5.27.3. เมื่อตัวตรวจจับทำงานผิดพลาด

วงกลมโดยเฉพาะได้ประโยชน์จาก ตัวกรองล่วงหน้า บนอินพุต เฟรมที่มีสัญญาณรบกวนมากจะมีพิกเซลขอบที่หลงเหลือจำนวนมากซึ่งต่างก็โหวต ส่งผลให้ Hough space มีจุดสูงสุดแบบกว้างและไม่ชัดเจนที่ตัวรวมแยกได้ยาก การผ่าน gaussian() หรือ mean() ก่อน find_circles() จะขจัดสัญญาณรบกวนและเหลือขอบจริงไว้ ตัวตรวจจับจะคืนจุดสูงสุดที่ชัดเจนขึ้นในเวลาที่สั้นลง

สำหรับสี่เหลี่ยม รูปแบบความล้มเหลวทั่วไปเป็นตรงกันข้าม ความเปรียบต่างต่ำระหว่างสี่เหลี่ยมกับพื้นหลังทำให้ผลรวมขนาดขอบไม่เคยผ่าน threshold การผ่าน histeq() เพื่อกระจายช่วงความสว่างทั่ว 0-255 จะคืนความเปรียบต่างที่ตัวตรวจจับต้องการ (ความเปรียบต่างต้องมีอยู่ ที่ใดที่หนึ่ง ในภาพ การปรับค่าฮิสโตแกรมสามารถขยายเฉพาะสิ่งที่มีอยู่แล้วเท่านั้น)