7.3. Hello BlazeFace¶
BlazeFace คือโครงข่ายประสาทเทียมสำหรับตรวจจับใบหน้าจากคอลเลกชัน MediaPipe ของ Google การเรียกใช้ inference ครั้งเดียวจะคืนค่ากรอบล้อมรอบรอบใบหน้าแต่ละใบหน้าที่ตรวจพบพร้อมกับจุดสำคัญบนใบหน้าหกจุด -- ตาขวา ตาซ้าย จมูก ปาก หูขวา หูซ้าย OpenMV Cam ทุกรุ่นที่จัดส่งพร้อมการรองรับโครงข่ายประสาทเทียมจะมีโมเดล blazeface_front_128.tflite อยู่บนแฟลช ดังนั้นการรัน face detector แบบครบวงจรต้องใช้โค้ด Python เพียงไม่กี่บรรทัด
7.3.1. สคริปต์เต็ม¶
import csi
import ml
from ml.postprocessing.mediapipe import BlazeFace
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((400, 400))
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
while True:
img = csi0.snapshot()
for (x, y, w, h), score, keypoints in model.predict([img]):
img.draw_rectangle((x, y, w, h), color=(0, 255, 0))
ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
นั่นคือ face detector ทั้งหมด ไม่มีอะไรเพิ่มเติม สคริปต์จับภาพเฟรม ส่งให้โมเดล วนผ่านรายการการตรวจจับที่ส่งกลับมา และวาดกรอบล้อมรอบและจุดสำคัญทั้งหกของแต่ละใบหน้ากลับลงในเฟรม ตัวอย่าง IDE แสดงกล่องและจุดสำคัญแบบเรียลไทม์
7.3.2. สิ่งที่แต่ละบรรทัดทำ¶
สามบรรทัดแรก import โมดูลที่สคริปต์ต้องการ csi คือ camera-sensor interface ml คือโมดูลแมชชีนเลิร์นนิงที่บทนี้กล่าวถึง BlazeFace คือ post-processor ที่แปลงเทนเซอร์เอาต์พุตดิบของ BlazeFace เป็นรายการ bounding-box และจุดสำคัญที่สคริปต์วนซ้ำ
ห้าบรรทัดถัดมาตั้งค่า sensor กล้องถูกรีเซ็ตกลับสู่สถานะที่ทราบ ตั้งค่าสีแบบ RGB565 ตั้งค่าความละเอียดเป็น VGA จากนั้น windowed เป็นกรอบสี่เหลี่ยมขนาด 400x400 หน้าต่างมีความสำคัญ: BlazeFace ได้รับการเทรนกับภาพตัดแบบสี่เหลี่ยม การให้อินพุตแบบสี่เหลี่ยมทำให้อัตราส่วนภาพที่โครงข่ายคาดหวังตรงกับสิ่งที่เห็นในเฟรมที่จับมา
บรรทัดโหลดโมเดลจะเปิดไฟล์โมเดล:
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
ml.Model อ่านไฟล์ที่เส้นทางที่กำหนด -- /rom/ คือระบบไฟล์ที่อยู่บนแฟลชซึ่งจะกล่าวถึงในภายหลัง -- และคืนค่า model object ที่สคริปต์จะรัน inference ด้วย คีย์เวิร์ด postprocess= ลงทะเบียน BlazeFace post-processor โดยที่ไม่มีมัน predict จะคืนค่าเทนเซอร์เอาต์พุตดิบของโครงข่ายและแอปพลิเคชันจะต้องถอดรหัสด้วยตัวเอง ด้วยมัน predict จะคืนค่าผลลัพธ์ที่ถอดรหัสแล้วโดยตรง อาร์กิวเมนต์ threshold=0.4 บน post-processor กำหนดความมั่นใจขั้นต่ำที่โครงข่ายต้องรายงานก่อนที่จะเก็บการตรวจจับ ค่าที่ต่ำกว่าจะตรวจจับใบหน้าที่ไม่ชัดได้มากขึ้นแต่แลกกับ false positive ที่มากขึ้น
สี่บรรทัดที่เหลือคือ main loop แต่ละรอบจะจับหนึ่งเฟรมและถามโมเดลว่าเห็นอะไร:
img = csi0.snapshot()
for (x, y, w, h), score, keypoints in model.predict([img]):
img.draw_rectangle((x, y, w, h), color=(0, 255, 0))
ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
predict() รับรายการอินพุต (ในที่นี้คือภาพที่จับมาหนึ่งภาพ) และคืนค่ารายการ tuple การตรวจจับ แต่ละ tuple เก็บกรอบล้อมรอบ (x, y, w, h) ค่าความมั่นใจ score ระหว่างศูนย์ถึงหนึ่ง และ ndarray ขนาด (6, 2) ของพิกัดจุดสำคัญ -- ตาขวา ตาซ้าย จมูก ปาก หูขวา และหูซ้ายตามลำดับนั้น การเรียกวาดใช้ draw_rectangle() -- primitive เดิมที่ตัวตรวจจับแบบ classical ทุกตัวในบทภาพจบด้วย -- เพื่อร่างกรอบรอบใบหน้า ml.utils.draw_keypoints() คือ helper เล็กๆ จาก ml utilities ที่ทำเครื่องหมายแต่ละ keypoint ด้วยเครื่องหมายบวกที่ตำแหน่ง (x, y)
7.3.3. สิ่งที่สคริปต์ไม่ได้บอก¶
สคริปต์มีเจ็ดบรรทัดที่รันได้ของงาน inference หลังจาก import และการตั้งค่า sensor แต่มีการคำนวณทางคณิตศาสตร์จำนวนมากเกิดขึ้นภายในเจ็ดบรรทัดนั้น เฟรม RGB565 ขนาด 400x400 ที่จับมากลายเป็นเทนเซอร์ 8 บิตที่ถูกควอนไทซ์ขนาด 128x128 ก่อนถึงโครงข่าย โครงข่ายรันการดำเนินการหลายร้อยครั้งกับน้ำหนักหลายหมื่นรายการ เทนเซอร์ที่ได้ของคะแนนความมั่นใจและค่า offset ของกล่องกลายเป็นรายการ bounding box ที่ไม่ซ้อนทับกันที่จัดอันดับแล้วพร้อมจุดสำคัญก่อนที่ predict จะส่งกลับ การแปลงแต่ละอย่างเหล่านี้เป็นสิ่งที่แอปพลิเคชัน สามารถ ควบคุมได้หากต้องการ และหลายอย่างต้องปรับจูนสำหรับโมเดลที่ไม่ใช่ค่าเริ่มต้น
สี่ส่วนย่อยถัดไปจะพาผ่านการแปลงเหล่านั้น ตามลำดับ:
The ml module -- สิ่งที่
ml.Modelเปิดเผยเมื่อโหลดโมเดลแล้ว และไฟล์โมเดลอยู่ที่ไหนบนกล้องThe inference pipeline -- สี่ขั้นตอนของทุกการเรียก
predict()Inference engines -- เส้นทาง CPU และ NPU ที่รันการคำนวณของโครงข่าย
Decoding the output -- post-processors ที่แปลงเทนเซอร์เอาต์พุตดิบเป็นการตรวจจับที่สคริปต์นี้วนซ้ำ
เมื่อจบบทนี้ ผู้อ่านสามารถเขียนสคริปต์เทียบเท่าสำหรับโมเดลที่ไม่ได้จัดส่งมาพร้อมกล้อง ถอดรหัสเทนเซอร์ที่ยังไม่มี post-processor และอธิบายได้ว่าทำไมโมเดลหนึ่งรันที่ 30 FPS บนกล้องหนึ่งและ 3 FPS บนอีกกล้อง