ESPIno32CAM : รู้จำใบหน้า
ตัวอย่างการใช้งาน ESPIno32CAM เพื่อถ่ายภาพ และ ตรวจหาพิกัดของใบหน้ามนุษย์ และ จดจำใบหน้า
เริ่มต้นการทดลอง
-เปิดโปรแกรม Arduino IDE
-เปิดตัวอย่าง File > Example >ESPIno32CAM > EX_Face_Recognition
– ตั้งค่า Arduino IDE ให้ใช้งานกับ ESPIno32CAM (Link)
– Upload Program ลงไปยัง ESPIno32CAM
– เปิด Program JPG Serial Streaming
1 เลือก Comport ให้ตรงกับ Comport ของบอร์ด ESPIno32
2 เลือก Baudrate ให้ตรงกับ Baudrate ใน Code Arduino (Default =2000000 )
3 กด Button Connect
!!! ก่อนกด Button Connect จะต้องปิด Program อื่นๆ ที่ใช้งาน Comport อยู่ เช่น หน้าต่าง Serial Monitor ของ ArduinoIDE เป็นต้น
– โปรแกรม JPG Serial Streaming จะแสดงรูปภาพที่ถ่ายจากบอร์ด ESPIno32CAM บน PictureBox และ เมื่อตรวจพบใบหน้าในภาพ จะแสดงข้อความ Intruder Alert (แจ้งเตือนผู้บุกรุก) เนื่องจากยังไม่มีการ Enroll ใบหน้า ทุกใบหน้าที่ตรวจพบ จึงถูกตีความหมายเป็นผู้บุกรุก
– Enroll ใบหน้าเข้าไปในระบบ โดย
1 นำกล้อง ถ่ายรูปใบหน้าให้สามารถตรวจจับใบหน้าได้
2 กด Button Program บนบอร์ดค้างไว้
3 รอจนขึ้นข้อความ ID x Sample 1 จึงปล่อยมือ แล้วรอจนกว่าจะ Enroll จนถึง ID x Sample 5
5 เมื่อ Enroll เสร็จเรียบร้อบแล้ว เมื่อโมดูลตรวจจับใบหน้าที่เรา Enroll ได้ก็จะแสดงเป็นหมายเลข ID ตามลำดับที่ได้ทำการ Enroll เอาไว้ เช่น Subject 0
ตัวอย่างโปรแกรมที่ใช้
#include "ESPino32CAM.h" ESPino32CAM cam; #define BUTTON 0 static mtmn_config_t mtmn_config = {0}; bool is_enrolling = false; static face_id_list id_list = {0}; void setup() { Serial.begin(2000000); cam.printDebug("\r\nESPino32CAM"); if (cam.init() != ESP_OK) { cam.printDebug(F("Fail")); return; } sensor_t *s = cam.sensor(); s->set_framesize(s, FRAMESIZE_QVGA); mtmn_config.min_face = 80; mtmn_config.pyramid = 0.7; mtmn_config.p_threshold.score = 0.6; mtmn_config.p_threshold.nms = 0.7; mtmn_config.r_threshold.score = 0.7; mtmn_config.r_threshold.nms = 0.7; mtmn_config.r_threshold.candidate_number = 4; mtmn_config.o_threshold.score = 0.7; mtmn_config.o_threshold.nms = 0.4; mtmn_config.o_threshold.candidate_number = 1; cam.faceIDInit(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES); } void loop() { if (digitalRead(BUTTON) == 0) { is_enrolling = true; } camera_fb_t *fb = cam.capture(); if (!fb) { cam.printDebug("Camera capture failed\r\n"); return; } dl_matrix3du_t *rgb888; if (cam.jpg2rgb(fb,&rgb888)) { cam.clearMemory(fb); box_array_t *net_boxes = cam.faceDetect(rgb888, &mtmn_config); if (net_boxes) { cam.drawFaceBoxes(rgb888, net_boxes, true); dl_matrix3du_t *aligned_face; if ( cam.alignFace(rgb888, net_boxes, &aligned_face) == ESP_OK) { if (is_enrolling) { int8_t left_sample_face = enroll_face(&id_list, aligned_face); cam.rgbPrintf(rgb888, FACE_COLOR_YELLOW, "ID[%u] Sample[%u]", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face); cam.printDebug("ID "+String(id_list.tail)+"Sample "+String(ENROLL_CONFIRM_TIMES - left_sample_face)); if (left_sample_face == 0){ is_enrolling = false; cam.rgbGoto(10,50); cam.rgbPrintf(rgb888, FACE_COLOR_YELLOW,"Enrolled Face ID: %d\n", id_list.tail); } if (left_sample_face == -2){ is_enrolling = false; cam.rgbGoto(10,50); cam.rgbPrintf(rgb888, FACE_COLOR_YELLOW,"Enrolled Fail\n", id_list.tail); } cam.rgbGoto(0,0); } else { int matched_id = cam.recognizeFace(&id_list, aligned_face); if (matched_id >= 0) { cam.printDebug("Match Face ID: "+String(matched_id)); cam.rgbPrintf(rgb888, FACE_COLOR_GREEN, "Hello Subject %u", matched_id); } else { cam.rgbPrintf(rgb888, FACE_COLOR_RED, "Intruder Alert!"); cam.printDebug("Intruder Alert!"); } } } cam.clearMemory(aligned_face); cam.clearMemory(net_boxes); } } uint8_t *jpgout; size_t jpglen; if (cam.rgb2jpg(rgb888,&jpgout, &jpglen)) Serial.write(jpgout, jpglen); cam.clearMemory(jpgout); cam.clearMemory(rgb888); }
คำอธิบายโปรแกรม
บรรทัดที่ 1: ประกาศใช้ Library ESPIno32CAM
บรรทัดที่ 2: สร้าง Object cam
บรรทัดที่ 3: กำหนดให้ Button ที่ใช้รับ input สั่ง Enroll ใช้ io 0
บรรทัดที่ 4: สร้าง Structure mtmn_config ชื่อ mtmn_config
บรรทัดที่ 5: สร้างตัวแปร เก็บสถานะสั่งงานEnroll
บรรทัดที่ 6: สร้างตัวแปร face_id_list เพื่อเก็บใบหน้าที่ Enroll แล้ว
บรรทัดที่ 10: Initial โมดูลกล้อง
บรรทัดที่ 16: กำหนดให้ถ่ายภาพขนาด QVGA (320×240 Pixel)
บรรทัดที่ 17-26: กำหนดค่า threshold ให้กับ mtmn
บรรทัดที่ 27: initial face_id_list โดยกำหนด จำนวนใบหน้าสูงสุดที่เก็บได้ และ จำนวนครั้งที่ Enroll ต่อหนึ่งใบหน้า
FACE_ID_SAVE_NUMBER มีค่าเท่ากับ 7
ENROLL_CONFIRM_TIMES มีค่าเท่ากับ 5
บรรทัดที่ 31-34: ตรวจสอบว่ามีการกด Button ที่ IO 0 หรือไม่ ถ้าหากมีการกด จะกำหนดให้ตัวแปร is_enrolling = true
บรรทัดที่ 35: ถ่ายรูปภาพ และ เก็บภาพ JPG ลงใน fb
บรรทัดที่ 42: แปลง JPG File ให้อยู่ในรูปแบบ RGB (R 8 bits , G 8 bits , B 8 bits) แล้วเก็บลงใน dl_matrix3du_t *rgb888
บรรทัดที่ 44: คืนค่า Memory ตัวแปล fb ที่เก็บ jpg ให้ส่วนกลาง
บรรทัดที่ 45: นำภาพที่แปลงเป็น RGB 888 และ ค่า mtmn_config_t เข้าไปประมวลผลหาใบหน้าในภาพ แล้วเก็บผลลัพธ์ลงใน net_boxes
บรรทัดที่ 46: ถ้าหากตรวจพบใบหน้า net_boxes จะมีค่ามากกว่า 0
บรรทัดที่ 48: วาดภาพกรอบปิดล้อมใบหน้า
บรรทัดที่ 50: จัดเรียงข้อมูลใบหน้า
บรรทัดที่ 52: ตรวจสอบตัวแปร is_enrolling ว่ามีการสั่งให้ Enroll หรือไม่
บรรทัดที่ 54: Enroll ใบหน้า โดย Function enroll_face จะ Return จำนวนครั้ง ที่เหลือที่ต้อง Enroll
บรรทัดที่ 55: เขียนข้อความแสดงสถานะการ Enroll ลงบนภาพในตัวแปร rgb888
id_list.tail = หมายเลข ID ของใบหน้าที่กำลัง Enroll
ENROLL_CONFIRM_TIMES – left_sample_face = จำนวนครั้งที่กำลัง Enroll ใบหน้า
บรรทัดที่ 56: ส่งข้อความแสดงสถานะการ Enroll ออกไปทาง Serial เพื่อแสดงบน Program JPG Serial Streaming
บรรทัดที่ 57: ตรวจสอบ ว่า Enroll เสร็จแล้วหรือไม่ จากค่าในตัวแปร left_sample_face จะมีค่าเท่ากับ 0
บรรทัดที่ 58: ให้ is_enrolling = false ยกเลิก Enroll
บรรทัดที่ 59: กำหนดตำแหน่ง x,y จุดเริ่มต้น สำหรับเขียนข้อความลงในภาพ
บรรทัดที่ 60: เขียนข้อความ แสดงหมายเลข ID ที่ Enroll เสร็จแล้วลงในภาพ
บรรทัดที่ 62: ตรวจสอบ ค่าในตัวแปร left_sample_face ถ้าหากมีค่า เท่ากับ – 2 ซึ่งหมายถึงการ Enroll ผิดพลาดหรือไม่
บรรทัดที่ 71: เปรียบเทียบใบหน้าที่ตรวจจับได้ว่าตรงกับ ใบหน้าใดใน id_list หรือไม่ และ Return หมายเลข ID ของใบหน้าที่ตรงกับใบหน้าใน id_list มาเก็บในตัวแปร matched_id
บรรทัดที่ 72: ถ้าหาก matched_id มีค่า มากกว่าหรือเท่ากับ 0 หมายถึงมีใบหน้าที่ตรงกับใบหน้าใน id_list
บรรทัดที่ 74-75: แสดงข้อความ หมายเลข ID ของใบหน้าที่ตรงกับใบหน้าใน id_list
บรรทัดที่ 79-80: แสดงข้อความ “Intruder Alert!” เนื่องจากไม่พบใบหน้าที่ตรงกับใบหน้าใน id_list
บรรทัดที่ 84-85: คืนค่า Memory
บรรทัดที่ 90 : แปลงภาพ ในรูปแบบ RGB888 ในตัวแปร rgb888 ให้เป็น JPG
บรรทัดที่ 91 : ส่งรูปภาพ JPG ออกไปทาง Serial เพื่อแสดงภาพบน Program JPG Serial Streaming
บรรทัดที่ 92-93: คืนค่า Memory
ในการทดลองข้างต้น จะเห็นว่าเราเก็บค่า face_id_list id_list เอาไว้ใน RAM เมื่อเราไม่ได้จ่ายไฟให้กับ ESPIno32CAM ก็จะทำให้ ใบหน้าใน id_list หายไป
ทางเราจึงได้ทำ ตัวอย่าง ที่ชื่อ Ex_Face_Recognition_save_to_SPIFFS
สามารถดูตัวอย่างได้ที่
File > Example >ESPIno32CAM > Ex_Face_Recognition_save_to_SPIFFS
ในตัวอย่างนี้จะใช้วิธีการ Save Data ที่อยู่ภายใน face_id_list ลงไป บน Flash Memory โดยใช้ SPIFFS สร้างเป็น File ขึ้นมา
#include "ESPino32CAM.h" ESPino32CAM cam; #define BUTTON 0 static mtmn_config_t mtmn_config = {0}; bool is_enrolling = false; static face_id_list id_list = {0}; void setup() { Serial.begin(2000000); cam.printDebug("\r\nESPino32CAM"); if (cam.init() != ESP_OK) { cam.printDebug(F("Fail")); return; } sensor_t *s = cam.sensor(); s->set_framesize(s, FRAMESIZE_QVGA); mtmn_config.min_face = 80; mtmn_config.pyramid = 0.7; mtmn_config.p_threshold.score = 0.6; mtmn_config.p_threshold.nms = 0.7; mtmn_config.r_threshold.score = 0.7; mtmn_config.r_threshold.nms = 0.7; mtmn_config.r_threshold.candidate_number = 4; mtmn_config.o_threshold.score = 0.7; mtmn_config.o_threshold.nms = 0.4; mtmn_config.o_threshold.candidate_number = 1; cam.faceIDInit(&id_list, FACE_ID_SAVE_NUMBER, ENROLL_CONFIRM_TIMES); if (cam.faceIDInitFlash()) { //cam.deleteFaceIDinFlash(); //Delete face id list if (!cam.readFaceIDFromFlash(&id_list)) cam.printDebug("Load face id list fail"); } else cam.printDebug("SPIFFS Fail"); } void loop() { if (digitalRead(BUTTON) == 0) { is_enrolling = true; } camera_fb_t *fb = cam.capture(); if (!fb) { cam.printDebug("Camera capture failed\r\n"); return; } dl_matrix3du_t *rgb888; if (cam.jpg2rgb(fb, &rgb888)) { cam.clearMemory(fb); box_array_t *net_boxes = cam.faceDetect(rgb888, &mtmn_config); if (net_boxes) { cam.drawFaceBoxes(rgb888, net_boxes, true); dl_matrix3du_t *aligned_face; if ( cam.alignFace(rgb888, net_boxes, &aligned_face) == ESP_OK) { if (is_enrolling) { int8_t left_sample_face = enroll_face(&id_list, aligned_face); cam.rgbPrintf(rgb888, FACE_COLOR_YELLOW, "ID[%u] Sample[%u]", id_list.tail, ENROLL_CONFIRM_TIMES - left_sample_face); cam.printDebug("ID " + String(id_list.tail) + "Sample " + String(ENROLL_CONFIRM_TIMES - left_sample_face)); if (left_sample_face == 0) { is_enrolling = false; cam.rgbGoto(10, 50); cam.rgbPrintf(rgb888, FACE_COLOR_YELLOW, "Enrolled Face ID: %d\n", id_list.tail); cam.writeFaceIDToFlash(&id_list); } if (left_sample_face == -2) { is_enrolling = false; cam.rgbGoto(10, 50); cam.rgbPrintf(rgb888, FACE_COLOR_YELLOW, "Enrolled Fail\n", id_list.tail); } cam.rgbGoto(0, 0); } else { int matched_id = cam.recognizeFace(&id_list, aligned_face); if (matched_id >= 0) { cam.printDebug("Match Face ID: " + String(matched_id)); cam.rgbPrintf(rgb888, FACE_COLOR_GREEN, "Hello Subject %u", matched_id); } else { cam.rgbPrintf(rgb888, FACE_COLOR_RED, "Intruder Alert!"); cam.printDebug("Intruder Alert!"); } } } cam.clearMemory(aligned_face); cam.clearMemory(net_boxes); } } uint8_t *jpgout; size_t jpglen; if (cam.rgb2jpg(rgb888, &jpgout, &jpglen)) Serial.write(jpgout, jpglen); cam.clearMemory(jpgout); cam.clearMemory(rgb888); }
การทำงานโดยหลักๆแล้วจะไม่แตกต่างจากการใช้งานเดิม เพียงแต่เพิ่ม Code ในส่วนการ อ่าน , เขียน ข้อมูลใบหน้าลงไปยัง SPIFFS เท่านั้น โดยจะมีส่วนที่แตกต่างจากของเดิม ดังนี้
บรรทัดที่ 27: initial face_id_list โดยกำหนด จำนวนใบหน้าสูงสุดที่เก็บได้ และ จำนวนครั้งที่ Enroll ต่อหนึ่งใบหน้า
บรรทัดที่ 28: faceIDInitFlash() เพื่อ initial เริ่มต้นใช้งาน SPIFFS ถ้าหากสามารถ initial สำเร็จ จะ return true
บรรทัดที่ 30: deleteFaceIDinFlash() ตัวอย่างคำสั่งหากต้องการลบ File ข้อมูลใบหน้าออกจาก SPIFFS
บรรทัดที่ 31: อ่านค่า File ข้อมูลใบหน้าที่ถูกเก็บอยู่ใน SPIFFS แล้ว Copy มาเก็บในตัวแปร face_id_list ถ้าหากอ่านค่าได้สำเร็จจะ return true
บรรทัดที่ 69: ทุกครั้งที่ Enroll สำเร็จ ใช้ Function writeFaceIDToFlash เก็บข้อมูลของใบหน้าลงไปยัง SPIFFS