7.13. การระงับค่าสูงสุดที่ซ้อนทับ (Non-max suppression)

โครงข่ายการตรวจจับมักสร้างกรอบล้อมรอบผู้สมัครหลายกรอบที่ซ้อนทับกันรอบวัตถุจริงชิ้นเดียวกัน ทุก anchor ที่อยู่ใกล้วัตถุจะให้คะแนนคล้ายกัน และตัวประมวลผลหลังจะเห็นทั้งหมด Non-max suppression (NMS) คือขั้นตอนที่เปลี่ยนกลุ่มนั้นให้เหลือกรอบเดียว

ขั้นตอนของอัลกอริทึมนั้นสั้น: เรียงกรอบผู้สมัครตามคะแนน เลือกกรอบที่มีคะแนนสูงสุด ระงับกรอบอื่น ๆ ที่ซ้อนทับกับกรอบนั้นเกินค่าขีดแบ่งที่กำหนด จากนั้นเลือกกรอบที่สูงสุดถัดไปจากส่วนที่เหลือ และทำซ้ำ ตัวชี้วัดการซ้อนทับคือ intersection-over-union (IoU) -- พื้นที่ร่วมของกรอบสองกรอบหารด้วยพื้นที่รวม มีค่าระหว่าง 0 (ไม่ซ้อนทับ) ถึง 1 (กรอบเดียวกัน) อาร์กิวเมนต์ตัวสร้าง nms_threshold บนตัวประมวลผลหลังทุกตัวที่ส่งมาคือค่าตัดที่กรอบจะถูกถือว่าเป็นซ้ำซ้อนของกรอบที่เก็บไว้แล้ว

Left, three overlapping bounding boxes around a single subject, labelled with scores 0.92, 0.83, and 0.71. An arrow points to the right, where one surviving box remains with the score 0.92. The two lower-scoring boxes are suppressed by the higher-scoring one because their intersection-over-union with it exceeds the threshold.

NMS ยุบกลุ่มการตรวจจับที่ซ้อนทับกันให้เหลือกรอบที่มีคะแนนสูงสุดกรอบเดียว

7.13.1. Soft-NMS

คลาส ml.utils.NMS ที่ส่งมานั้นใช้ Soft-NMS ซึ่งเป็นการปรับปรุงที่ลดคะแนนของกรอบที่ซ้อนทับลงตามปริมาณที่ซ้อนทับ แทนที่จะตัดทิ้งกรอบนั้นทันที หากคะแนนที่ลดลงต่ำกว่าค่าขีดแบ่ง กรอบนั้นจะถูกตัดทิ้ง มิฉะนั้นจะรอดพ้นด้วยคะแนนที่ลดลงและแข่งขันในรอบถัดไป

พารามิเตอร์ nms_sigma ควบคุมความเข้มข้นของการลดคะแนน หาก nms_sigma มีค่าน้อย (ค่าเริ่มต้นที่ส่งมาคือ 0.1) การลดคะแนนจะชันมาก กรอบที่ซ้อนทับมากจะมีคะแนนลดลงเกือบเป็นศูนย์ และ Soft-NMS จะทำงานเหมือน NMS แบบดั้งเดิม หาก nms_sigma มีค่ามากขึ้น การลดคะแนนจะเบาลง และกรอบที่ซ้อนทับของวัตถุต่างกันจะรอดพ้นบ่อยขึ้น ซึ่งสำคัญเมื่อวัตถุในโลกจริงของคลาสเดียวกันซ้อนทับกันจริง (ฝูงใบหน้า กลุ่มฝ่ามือ)

การตั้งค่า nms_sigma เป็น <= 0 จะปิดการลดคะแนนทั้งหมด กรอบที่ซ้อนทับจะผ่านไปพร้อมคะแนนเดิม และมีเพียงค่าขีดแบ่งคะแนนเท่านั้นที่กรองพวกมัน

7.13.2. สร้างโดยตรง

ตัวประมวลผลหลังทุกตัวที่ส่งมาจะสร้าง NMS ใหม่ต่อการอนุมานหนึ่งครั้ง เพิ่มผู้สมัครแต่ละรายเข้าไป และเรียก get_bounding_boxes() ในตอนท้าย ตัวประมวลผลหลังแบบกำหนดเองทำตามรูปแบบเดียวกัน:

from ml.utils import NMS

iw = model.input_shape[0][2]
ih = model.input_shape[0][1]

nms = NMS(iw, ih, inputs[0].roi)
for box, score, class_idx in candidates:
    nms.add_bounding_box(box.xmin, box.ymin,
                         box.xmax, box.ymax,
                         score, class_idx)
result = nms.get_bounding_boxes(threshold=nms_threshold,
                                sigma=nms_sigma)

ตัวสร้างรับความกว้างและความสูงอินพุตของโครงข่ายในหน่วยพิกเซล และบริเวณที่สนใจ (ROI) ของภาพต้นฉบับที่โมเดลรันกับ add_bounding_box() รับพิกัดกรอบในพื้นที่พิกเซลอินพุตของโครงข่ายนั้น และ get_bounding_boxes() แมปผู้รอดพ้นกลับไปยังพิกัดของภาพโดยใช้ ROI การแมปจะคำนึงถึง Normalization stretch โดยอัตโนมัติ -- ROI เดียวกับที่ตัวทำนายเห็นจะถูกใช้ในการฉายกรอบกลับ -- ดังนั้นกรอบที่ส่งคืนจึงพร้อมที่จะวาดลงบนเฟรมที่ถ่ายมา

รูปแบบค่าที่ส่งคืนเป็นรายการของรายการต่อคลาส โดยดัชนีตาม label_index ที่ส่งไปยัง add_bounding_box รายการคลาสที่ว่างเปล่าจะถูกเก็บไว้เพื่อให้ดัชนีตรงกับดัชนีคลาสของโมเดล enumerate(result) จะวนซ้ำคลาสพร้อมกับการตรวจจับของพวกมัน