การตรวจจับใบหน้า (Face detection) เปรียบเสมือนประตูบานแรกของ pipeline ในงาน computer vision ส่วนใหญ่ ไม่ว่าจะเป็นการโทรวิดีโอคอล, แกลเลอรีรูปภาพ หรือฟีดจากกล้องวงจรปิด ทุกอย่างล้วนต้องอาศัยการตรวจจับใบหน้ามนุษย์ก่อนที่จะดำเนินการขั้นตอนอื่นได้ อย่างไรก็ตาม การเลือกโมเดลมักจะนำไปสู่การแลกเปลี่ยนที่ไม่น่าพึงพอใจ (unhappy trade-off) เสมอ โมเดลขนาดใหญ่ให้ความแม่นยำสูงแต่ต้องแลกมาด้วย GPU ราคาแพงและระบบระบายความร้อนแบบติดตั้งในตู้แร็ค ส่วนโมเดลขนาดเล็กสามารถรันบนฮาร์ดแวร์ทั่วไปได้ แต่ก็มักจะติดขัดเมื่อต้องเจอกับใบหน้าขนาดเล็กหรือฟีดที่มีความละเอียดสูง โมเดล YuNet จาก OpenCV Zoo ได้เข้ามาทำลายรูปแบบเดิมๆ นั้น ผมจึงใช้เวลาในการทำ benchmark กับโมเดลนี้ในสเกลที่แตกต่างกัน เพื่อดูว่ามันคู่ควรกับการนำไปใช้ในโปรเจกต์จริง หรือเป็นเพียงโมเดลที่ดูดีแค่ในหน้ากระดาษ

ทำไม YuNet ถึงควรค่าแก่การพิจารณาอย่างใกล้ชิด

YuNet ไม่ใช่แค่ผลงานวิจัยที่น่าสนใจเล่นๆ แต่มันถูกบรรจุอยู่ใน OpenCV Zoo ซึ่งเป็นชุดรวมโมเดลที่ผ่านการฝึกฝนมาล่วงหน้า (pre-trained models) และปรับแต่งมาเพื่อโมดูล OpenCV DNN โดยเฉพาะ โมเดลเวอร์ชันที่ผมทดสอบนั้นใช้ INT8 quantization ซึ่งหมายความว่า weights และ activations จะถูกบีบอัดลงเหลือเพียง integer ขนาด 8-bit แทนที่จะเป็น floating-point ขนาด 32-bit ตามปกติ นี่ไม่ใช่แค่รายละเอียดทางเทคนิคเล็กๆ น้อยๆ เพราะ INT8 ช่วยลดการใช้ memory bandwidth, ลดภาระของ cache และช่วยให้ CPU สมัยใหม่สามารถประมวลผล inference ได้อย่างรวดเร็วโดยไม่ต้องออกแรงมาก สำหรับใครก็ตามที่กำลังพัฒนาแอปพลิเคชันบนแล็ปท็อป, อุปกรณ์ edge หรือเซิร์ฟเวอร์ที่ไม่มีการ์ดจอแยก ประสิทธิภาพนี้คือตัวตัดสินระหว่าง pipeline ที่ทำงานลื่นไหลกับระบบที่ทำงานติดขัดเหมือนดูสไลด์โชว์

ตัวสถาปัตยกรรมเองถูกออกแบบมาให้มีน้ำหนักเบา โดยตัดส่วนประกอบที่ไม่จำเป็นของ backbone ขนาดมหึมาออกไป และมุ่งเน้นไปที่งานเดียวคือการระบุตำแหน่งใบหน้า ความมีวินัยในการออกแบบนี้จะส่งผลดีเมื่อคุณต้องรวมการตรวจจับใบหน้าเข้ากับแอปพลิเคชันขนาดใหญ่ ซึ่งต้องแบ่งทรัพยากร CPU และพลังงานแบตเตอรี่ร่วมกับการทำ tracking, การจดจำใบหน้า (recognition) หรือการเข้ารหัสวิดีโอ (video encoding)

การตั้งค่าการทดสอบ

การทดสอบทั้งหมดทำบน Mac Studio ที่ใช้ชิป Apple M4 Max โดยใช้ไฟล์โมเดล YuNet แบบ INT8 quantized ONNX จาก repository ของ OpenCV Zoo ผมเริ่มจากการวัดความเร็วในการทำ inference บนภาพขนาด 1280x720 จากนั้นจึงเปรียบเทียบจำนวนการตรวจจับในความละเอียดของอินพุตที่แตกต่างกัน 4 ระดับ เพื่อทำความเข้าใจว่าสเกลของภาพส่งผลต่อผลลัพธ์อย่างไร

หากคุณต้องการตรวจสอบตัวเลขเหล่านี้บนเครื่องของคุณเอง กระบวนการนี้สามารถทำซ้ำได้อย่างสมบูรณ์ เพียงรันคำสั่งต่อไปนี้เพื่อดึง sparse repository และเริ่มการทำ benchmark:

git clone --depth 1 --filter=blob:none --sparse https://github.com/kiarina/labs.git
cd labs
git sparse-checkout set .gitignore .mise/tasks Makefile mise.toml 2026/07/08/yunet-face-detection
mise -C 2026/07/08/yunet-face-detection run

การใช้ sparse checkout จะช่วยให้การดาวน์โหลดมีขนาดเล็ก และตัวรันงาน mise จะจัดการเรื่อง dependencies ให้เบื้องหลัง คุณไม่จำเป็นต้องวุ่นวายกับการจัดการ Python environments หรือการติดตั้งแพ็กเกจด้วยตัวเอง

ความเร็วที่สม่ำเสมอ

บนภาพขนาด 1280x720 โมเดล YuNet แบบ INT8 มีค่าเฉลี่ยการทำ inference อยู่ที่ 9.21 มิลลิวินาที โดยรอบที่เร็วที่สุดอยู่ที่ 8.03 ms ในขณะที่ช้าที่สุดอยู่ที่ 10.47 ms ซึ่งถือว่าเป็นช่วงที่แคบมาก โดยมีความต่างระหว่างเวลาที่ดีที่สุดและแย่ที่สุดไม่ถึงสองมิลลิวินาทีครึ่ง

ในทางปฏิบัติ ความสม่ำเสมอนี้สำคัญกว่าการโอ้อวดตัวเลขความเร็ว แอปพลิเคชันแบบ real-time ไม่ได้ต้องการแค่ค่า latency เฉลี่ยที่ต่ำ แต่ต้องการ latency ที่คาดเดาได้ โมเดลที่บางครั้งใช้เวลาถึง 50 ms จะทำให้เกิดอาการกระตุกที่เห็นได้ชัดในฟีดเว็บแคม แต่ YuNet ให้ผลลัพธ์ที่คงที่ คุณสามารถมั่นใจได้ว่ามันจะประมวลผลเฟรมเสร็จก่อนที่เฟรมถัดไปจะมาถึง ซึ่งช่วยให้การจัดตารางเวลา (scheduling) ของ pipeline ส่วนที่เหลือทำได้ง่ายขึ้นมาก

ความแปรผันของขนาดเผยให้เห็นความจริง

ความเร็วเพียงอย่างเดียวแทบไม่มีความหมายหากโมเดลตรวจจับใบหน้าได้ไม่ครบในฉากนั้นๆ เพื่อทดสอบเรื่องนี้ ผมจึงส่งภาพต้นฉบับชุดเดียวกันผ่าน YuNet ที่ความละเอียดต่างกัน 4 ระดับ ซึ่งผลลัพธ์ที่ได้บอกเล่าเรื่องราวได้อย่างชัดเจน:

  • 320 x 180: ตรวจพบ 6 ใบหน้า
  • 640 x 360: ตรวจพบ 26 ใบหน้า
  • 1280 x 720: ตรวจพบ 38 ใบหน้า
  • 2560 x 1440: ตรวจพบ 52 ใบหน้า

ตัวเลขที่เพิ่มขึ้นนั้นน่าทึ่งมาก ที่ความละเอียด 320 x 180 จะมีเพียงใบหน้าที่ใหญ่ที่สุดและอยู่ใกล้ที่สุดเท่านั้นที่ถูกตรวจพบ แต่เมื่อขยับขึ้นมาเป็น 640 x 360 จำนวนที่ตรวจพบจะเพิ่มขึ้นถึงสี่เท่า และที่ความละเอียด 1440p เต็มรูปแบบ YuNet สามารถหาใบหน้าได้มากกว่าตอนความละเอียดต่ำสุดถึงแปดเท่าตัว

สิ่งนี้เกิดขึ้นเพราะการทำ downsampling ทำลายรายละเอียดเร็วกว่าที่คิด ใบหน้าที่เคยมีพื้นที่พอสมควรในเฟรม 1440p อาจหดเล็กลงจนเหลือเพียงจุดเบลอขนาด 5x5 พิกเซลที่ความละเอียด 360p เมื่อลักษณะเด่นบนใบหน้าลดลงจนต่ำกว่า receptive field ของโมเดล พวกมันจะกลายเป็นเพียง noise ที่มองไม่เห็น ดวงตาจะกลืนไปกับคิ้ว จมูกจะหายไป และ YuNet ก็ไม่มีข้อมูลเพียงพอที่จะจับจุดสำคัญได้

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

กลยุทธ์การปรับขนาด (Resize) ที่คุณต้องการจริงๆ

YuNet เร็วพอที่คุณไม่จำเป็นต้องลดขนาดอินพุตลงเหลือ 320 x 240 ตามความเคยชิน บนชิป M4 Max แม้แต่ความละเอียด 2560 x 1440 ก็สามารถรันได้โดยไม่ต้องใช้ GPU แยก สิ่งนี้จะเปลี่ยนวิธีการออกแบบ pipeline ของคุณ

แทนที่จะบังคับให้ทุกเฟรมต้องผ่านขนาดคงที่ขนาดเล็ก ให้เลือกความละเอียดอินพุตตามสิ่งที่คุณกำลังพยายามค้นหา หากคุณสนใจเพียงแค่คนที่อยู่หน้ากล้องโดยตรง ความละเอียด 720p หรือแม้แต่ 640p ก็เพียงพอแล้ว แต่ถ้าคุณต้องการจัดทำรายการผู้เข้าร่วมทุกคนในห้องโถงขนาดใหญ่ ให้ป้อนภาพที่ครอปมาด้วยความละเอียดสูงกว่าเดิมหรือใช้เฟรมต้นฉบับทั้งหมด เนื่องจาก YuNet มีน้ำหนักเบา คุณจึงมีพื้นที่ว่าง (headroom) ในการตัดสินใจเลือกแบบนั้น คุณไม่ต้องถูกจำกัดอยู่กับค่าพรีเซ็ตเดียวเพื่อหลีกเลี่ยงอาการหน่วง 200 ms

อย่างไรก็ตาม ยังมีข้อควรระวังหนึ่งประการ คือโมเดลยังคงขึ้นอยู่กับขนาดใบหน้าเมื่อเทียบกับ input tensor ที่นี่ไม่มีความสามารถในการปรับขนาดอัตโนมัติ (scale invariance) ที่เป็นเวทมนตร์ ใบหน้าขนาดเล็กจะยังคงมองไม่เห็นจนกว่าจะมีจำนวนพิกเซลที่มากพอ วิธีแก้ไขนั้นเป็นเรื่องทางเทคนิค: ให้ขยายขนาดภาพต้นฉบับขึ้นก่อนการทำ inference เมื่อต้องการค้นหาวัตถุที่อยู่ไกล จากนั้นจึงแมป bounding boxes ที่ได้กลับไปยังพิกัดเดิม YuNet มอบงบประมาณด้านความเร็ว (speed budget) ให้คุณสามารถทำขั้นตอนนั้นได้

ตำแหน่งของมันใน Stack ของคุณ

YuNet ไม่ใช่คำตอบสำหรับทุกงานที่เกี่ยวข้องกับใบหน้า การถูกบดบังอย่างหนัก มุมด้านข้างที่รุนแรง หรือการสกัด 3D mesh นั้นอยู่นอกเหนือขอบเขตของมัน แต่สำหรับการทำงานหลักๆ อย่างการระบุตำแหน่งใบหน้าในภาพถ่ายและวิดีโอสตรีม มันอยู่ในจุดที่ลงตัวที่สุด (sweet spot) ไม่ว่าจะเป็นแอปเว็บแคมแบบเรียลไทม์ เครื่องมือคัดกรองภาพถ่ายอัตโนมัติ หรือระบบฝังตัว (embedded systems) ขนาดเล็ก ต่างก็ได้รับประโยชน์จากตัวตรวจจับที่ทำงานได้รวดเร็วบน CPU มาตรฐาน

รูปแบบ INT8 ONNX ยังช่วยให้การ deployment เป็นเรื่องง่าย คุณไม่จำเป็นต้องติดตั้ง PyTorch หรือ TensorFlow บนอุปกรณ์เป้าหมาย โมดูล DNN ของ OpenCV สามารถโหลดโมเดลได้โดยตรง ซึ่งช่วยให้ไฟล์ binary ของคุณมีขนาดเล็กและลดความซับซ้อนของ dependency tree

บทสรุป

YuNet พิสูจน์ให้เห็นว่าการตรวจจับใบหน้าไม่จำเป็นต้องใช้ฮาร์ดแวร์ระดับอุตสาหกรรมหรือเฟรมเวิร์กที่เทอะทะ ตัวเลขนั้นบ่งบอกอย่างชัดเจน: ใช้เวลาไม่ถึงสิบมิลลิวินาทีสำหรับเฟรม 720p และจำนวนการตรวจจับจะเพิ่มขึ้นอย่างตรงไปตรงมาและคาดการณ์ได้เมื่อความละเอียดสูงขึ้น คุณสามารถเลือกได้ว่าต้องการความเร็วสูงสุดที่ความละเอียดปานกลาง หรือต้องการการครอบคลุมที่กว้างขึ้นในสเกลที่สูงกว่า และโมเดลจะไม่ส่งผลเสียต่อประสิทธิภาพจากการตัดสินใจนั้น

หากคุณกำลังสร้างอะไรก็ตามที่เกี่ยวข้องกับภาพในโลกแห่งความเป็นจริง ให้ลองทำตามขั้นตอนการทดสอบด้านบนบนฮาร์ดแวร์ของคุณเอง ทดสอบกับฟุตเทจของคุณ จากนั้นจึงปรับจูนตรรกะการ resize ให้เหมาะสมกับใบหน้าที่คุณต้องการค้นหาจริงๆ ความเร็วจะมีประโยชน์ก็ต่อเมื่อคุณสามารถควบคุมทิศทางของมันได้ YuNet มอบให้คุณทั้งความเร็วและการควบคุม