ESPIno32CAM : FaceDetect

ESPIno32CAM : ตรวจจับใบหน้า

ตัวอย่างการใช้งาน ESPIno32CAM เพื่อถ่ายภาพ และ ตรวจหาพิกัดของใบหน้ามนุษย์ภายในภาพ 

เริ่มต้นการทดลอง

– เปิดโปรแกรม Arduino IDE

– เปิดตัวอย่าง File > Example >ESPIno32CAM > EX_FaceDetect

– ตั้งค่า 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 และ ถ้าหากสามารถตรวจจับใบหน้าภายในภาพได้ จะแสดงกรอบล้อมรอบใบหน้า และ แสดงพิกัดจุด landmark 5 จุด

   ภายในส่วนแสดงข้อความก็จะแสดงข้อมูล จำนวนใบหน้าที่พบ  , ID ของใบหน้า , พิกัดเริ่มต้นของใบหน้า และ ขนาด กว้าง,ยาว ของกรอบ 

หลักการทำงาน

                Face Detection ของ ESP32 นั้นใช้โมเดลการตรวจจับใบหน้าที่ชื่อว่า MTMN ซึ่งเป็นโมเดลที่ใช้ทรัพยากรณ์ในการประมวลผลตรวจจับใบหน้าน้อย ออกแบบมาสำหรับใช้งานบน Embedded Device ซึ่งมีการทำงานแบ่งออกเป็น 3 ส่วนหลักๆได้แก่

– Proposal Network (P-Net)

นำเสนอ bounding box ที่เป็นตัวเลือกส่งต่อไปยัง R-Net

– Refine Network (R-Net)

คัดเลือก bounding box ที่ P-Net ส่งมา

– Output Network (O-Net)

ส่งผลลัพธ์ออกมา เป็นค่า bounding box ที่ถูกต้องแม่นยำ ค่าความน่าเชื่อถือ และจุดกำหนดตำแหน่ง 5 จุด

 รูปภาพจาก https://arxiv.org/abs/1604.02878

การกำหนดค่าให้  MTMN จะต้องทำการตั้งค่า threshold ต่างๆผ่าน structure mtmn_config_t โดยภายใน structure mtmn_config_t จะประกอบด้วย ตัวแปร และ structure ภายในดังนี้

– float min_face

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

–  float pyramid

คือ ตัวแปรสำหรับกำหนดสเกลการ Resize ภาพ สามารถกำหนดได้ตั้งแต่ 0.0 – 1.0 โดยถ้าหากกำหนดค่า  pyramid มากขึ้นก็จำทำให้เกิดความละเอียดในการ ย่อรูปภาพมากขึ้น ส่งผลให้มีภาพทีต้องประมวลผลมากขึ้น เกิดการตรวจจับภาพใบหน้ามากขึ้น เวลาในการประมวลผลก็จะมากขึ้นเช่นกัน 

 – p_threshold , r_threshold , o_threshold 

คือ ค่า threshold สำหรับกำหนดการคัดกรอง ของ P-Net, R-Net, O-Net โดย

P-Net คือ ส่วนตรวจสอบและตีกรอบ พิกัดในภาพที่คาดว่าน่าจะเป็นส่วนที่มีใบหน้าอยู่ แล้วส่งไปยัง R-Net

R-Net คือ ส่วนที่คัดกรองข้อมูลที่ได้รับ P-Net กรองเอาเฉพาะข้อมูลที่ควนจะเป็นใบหน้า

O-Net คือ ส่วนที่นำเอาข้อมูลที่คัดกรองแล้วจาก R-Net มาหาค่าที่ถูกต้องแม่นยำ ค่าความน่าเชื่อถือ และจุดกำหนดตำแหน่ง 5 จุด (Landmark)

รูปภาพจาก https://arxiv.org/abs/1604.02878

Structure threshold_config_t

– score

คือค่า threshold ของ confidence coefficient (สัมประสิทธิ์ความ เชื่อมัน)ถ้า candidate bounding box มีค่าความน่าเชื่อถือน้อยกว่าค่าที่กำหนดไว้ จะถูกคัดกรองออกไป

                – ช่วงค่าที่กำหนดได้ 0.0 – 1.0

– nms

คือค่า threshold ของ NMS ถ้า candidate bounding box มี overlapping ratio สูงกว่าค่าที่กำหนดไว้จะถูกคัดกรองออกไป

                – ช่วงค่าที่กำหนดได้ 0.0 – 1.0

– Candidate

คือค่า จำนวนมากที่สุดของ bounding box ที่ให้มีได้แต่จะมีเพียงหมายเลขแรกเท่านั้นที่จะถูกเก็บค่าไว้

– ช่วงค่าที่กำหนดได้

R-Net กำหนดได้ 1-4

O-Net กำหนดได้ 1-2

P-Net  กำหนดได้ 4 เท่านั้น (ยังไม่ให้ผู้ใช้ปรับค่าได้ในตอนนี้)

ตัวอย่างโปรแกรมที่ใช้

#include "ESPino32CAM.h"
ESPino32CAM cam;
static mtmn_config_t mtmn_config = {0};
void setup() {
  Serial.begin(2000000);
  Serial.println("\r\nESPino32CAM");
  if (cam.init() != ESP_OK)
  {
    cam.printDebug(F("Init Fail"));
    while (1);
  }
  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 = 2;
}
void loop()
{
  camera_fb_t *fb = cam.capture();
  if (!fb)
  {
    cam.printDebug("Camera capture failed");
    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.printDebug("");
      cam.printfDebug("Found %d face",net_boxes->len);
      int x, y, w, h, i;
      for (i = 0; i < net_boxes->len; i++)
      {
        x = (int)net_boxes->box[i].box_p[0];
        y = (int)net_boxes->box[i].box_p[1];
        w = (int)net_boxes->box[i].box_p[2] - x + 1;
        h = (int)net_boxes->box[i].box_p[3] - y + 1;
        cam.printfDebug("Face:%d\r\nOrigin: (x:%d,y:%d)\r\nWide: %d\r\nHigh: %d",i,x,y,w,h);
      }
      cam.drawFaceBoxes(rgb888, net_boxes, true);
      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 Program 

บรรทัดที่ 1: ประกาศใช้ Library ESPIno32CAM

บรรทัดที่ 2: สร้าง Object cam

บรรทัดที่ 3: สร้าง Structure mtmn_config ชื่อ mtmn_config

บรรทัดที่ 7: Initial โมดูลกล้อง

บรรทัดที่ 13: กำหนดให้ถ่ายภาพขนาด QVGA (320×240 Pixel)

บรรทัดที่ 16-23: กำหนดค่า threshold ให้กับ mtmn เพิ่มเติม

บรรทัดที่ 27: ถ่ายรูปแล้วเก็บ JPG File ยัง fb

บรรทัดที่ 34: แปลง JPG File ให้อยู่ในรูปแบบ RGB (R 8 bits , G 8 bits , B 8 bits) แล้วเก็บลงใน dl_matrix3du_t *rgb888

บรรทัดที่ 36: คืนค่า Memory ตัวแปล fb ที่เก็บ jpg ให้ส่วนกลาง

บรรทัดที่ 37: นำภาพที่แปลงเป็น RGB 888 และ ค่า mtmn_config_t เข้าไปประมวลผลหาใบหน้าในภาพ แล้วเก็บผลลัพธ์ลงใน net_boxes

บรรทัดที่ 38: ถ้าหากตรวจพบใบหน้า net_boxes จะมีค่ามากกว่า 0

บรรทัดที่ 41: แสดงค่า จำนวนใบหน้าที่สามารถตรวจเจอจาก net_boxes->len

บรรทัดที่ 43-48: หาขนาดกว้าง,ยาว ของกรอบสี่เหลี่ยมรอบใบหน้าโดยตัวแปร box_p[0],box_p[1] คือ พิกัด x,y ของจุดเริ่มต้น และ box_p[2],box_p[3] คือพิกัดสิ้นสุด 

บรรทัดที่ 49: แสดงผล พิกัดจุดเริ่มต้น และ ความกว้างยาวของกรอบปิดล้อมใบหน้า

บรรทัดที่ 51: ใช้ function drawFaceBoxes เขียนกรอบ สี่เหลี่ยมปิดล้อมใบหน้าลงไปยัง รูปภาพในตัวแปร rgb888

บรรทัดที่ 52: คือค่า Memory net_boxes

บรรทัดที่ 58: ใช้ function rgb2jpg แปลงรูปภาพ RGB 888 กลับไปเป็น JPG File 

บรรทัดที่ 59: ส่งรูปภาพ JPG File ออกไปทาง Serial เพื่อไปแสดงผลยังโปรแกรม JPG Serial Streaming

บรรทัดที่ 60-61: คืน Memory