Face detection on a display

I have combined face detection code from CameraWebServer with camera code I made to get this:

#include "esp_camera.h"
#include <TFT_eSPI.h>
#include "pins.h"
#include <vector>
#include "human_face_detect_msr01.hpp"
#include "human_face_detect_mnp01.hpp"
#include "fb_gfx.h"

#define TWO_STAGE 1

TFT_eSPI tft = TFT_eSPI();

void setup() {
  Serial.begin(115200);
  Serial.println();
  cam_init();
  tft_init(1);
}

void loop() {
  face_detect();
}

void cam_init(){
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 10000000;
  config.pixel_format = PIXFORMAT_RGB565;
  config.frame_size = FRAMESIZE_QVGA; 
  config.jpeg_quality = 12;
  config.fb_count = 2;

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    while(1);
  }
  Serial.println("Camera init success!");

  sensor_t * s = esp_camera_sensor_get();

    // This is what looks best for me
    s->set_vflip(s, 1);
    s->set_brightness(s, -2);
    s->set_saturation(s, 3);
    s->set_hmirror(s, 1);
    s->set_contrast(s, 3);
    s->set_gainceiling(s, (gainceiling_t)511);
}

void draw_cam_buf() {
  camera_fb_t * fb = esp_camera_fb_get();
  if(!fb){
    Serial.println("Image capture error!");
    return;
  }  
  tft.pushImage(0, 0, fb->width, fb->height, (uint16_t *) fb->buf); 
  esp_camera_fb_return(fb);
}

void tft_init(int rtn){
  tft.begin();
  tft.setRotation(rtn);
  tft.fillScreen(TFT_BLACK);
}

static void draw_face_boxes(fb_data_t *fb, std::list<dl::detect::result_t> *results, int face_id)
{
    int x, y, w, h;
    uint32_t color = TFT_YELLOW;
    if (face_id < 0)
    {
        color = TFT_RED;
    }
    else if (face_id > 0)
    {
        color = TFT_GREEN;
    }
    if(fb->bytes_per_pixel == 2){
        //color = ((color >> 8) & 0xF800) | ((color >> 3) & 0x07E0) | (color & 0x001F);
        color = ((color >> 16) & 0x001F) | ((color >> 3) & 0x07E0) | ((color << 8) & 0xF800);
    }
    int i = 0;
    for (std::list<dl::detect::result_t>::iterator prediction = results->begin(); prediction != results->end(); prediction++, i++)
    {
        // rectangle box
        x = (int)prediction->box[0];
        y = (int)prediction->box[1];
        w = (int)prediction->box[2] - x + 1;
        h = (int)prediction->box[3] - y + 1;
        if((x + w) > fb->width){
            w = fb->width - x;
        }
        if((y + h) > fb->height){
            h = fb->height - y;
        }
        fb_gfx_drawFastHLine(fb, x, y, w, color);
        fb_gfx_drawFastHLine(fb, x, y + h - 1, w, color);
        fb_gfx_drawFastVLine(fb, x, y, h, color);
        fb_gfx_drawFastVLine(fb, x + w - 1, y, h, color);
#if TWO_STAGE
        // landmarks (left eye, mouth left, nose, right eye, mouth right)
        int x0, y0, j;
        for (j = 0; j < 10; j+=2) {
            x0 = (int)prediction->keypoint[j];
            y0 = (int)prediction->keypoint[j+1];
            fb_gfx_fillRect(fb, x0, y0, 3, 3, color);
        }
#endif
    }
}


void face_detect() {
  camera_fb_t * fb = esp_camera_fb_get();
  #if TWO_STAGE
        HumanFaceDetectMSR01 s1(0.1F, 0.5F, 10, 0.2F);
        HumanFaceDetectMNP01 s2(0.5F, 0.3F, 5);
        std::list<dl::detect::result_t> &candidates = s1.infer((uint16_t *)fb->buf, {(int)fb->height, (int)fb->width, 3});
        std::list<dl::detect::result_t> &results = s2.infer((uint16_t *)fb->buf, {(int)fb->height, (int)fb->width, 3}, candidates);
#else
        HumanFaceDetectMSR01 s1(0.3F, 0.5F, 10, 0.2F);
        std::list<dl::detect::result_t> &results = s1.infer((uint16_t *)fb->buf, {(int)fb->height, (int)fb->width, 3});
#endif
        if (results.size() > 0) {
            fb_data_t rfb;
            rfb.width = fb->width;
            rfb.height = fb->height;
            rfb.data = fb->buf;
            rfb.bytes_per_pixel = 2;
            rfb.format = FB_RGB565;
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_INFO
            detected = true;
#endif
            draw_face_boxes(&rfb, &results, 0);
        }
   tft.pushImage(0, 0, fb->width, fb->height, (uint16_t *) fb->buf); 
  esp_camera_fb_return(fb);
}

Interesting! How do we use it. How do we wire it?

There is no wiring. Use any board that comes with a camera preinstalled (like an ESP32-CAM!). For usage, show the camera a human face (drawings do kinda work), and check for a yellow box with 5 points inside. These 5 points indicate landmarks (left eye, mouth left, nose, right eye, mouth right).

Is it working? Have you tested the code and the setup?

Yes, this worked with my ESP32-WROVER from Freenove with an OV3660. 2 faces also worked! However, this sometimes crashes in certain face positions.

This means it does not work. What testing have you done toward making it work? What is your goal?

Are you hoping to fix the problems, or just showcasing a faulty project?

I hope I could fix it, but for now, I'm unsure why it happens.

I did have some problems when copying code from CameraWebServer, but it turns out <vector> was missing, and I had to include "fb_gfx.h" because simply writing to the display had an issue (I probably did something else wrong but resolved it after I included "fb_gfx.h"). My goal is to make the face detection work without random crashing in certain face positions. I have also made some code for cat faces, but I don't know if that crashed.

Use Serial.print("HERE") in places you suspect the crash, or systematically use serial print, starting in setup() and move it (serial print) as functions work to narrow the search for the crashes.

Also (unrelated), how do you use s->set_res_raw(); ?

Totally off topic and without context.
Find the crashing point.

The crashing point is at draw_face_boxes(&rfb, &results, 0);, which spits out this:

Guru Meditation Error: Core  1 panic'ed (LoadStoreError). Exception was unhandled.

Core  1 register dump:
PC      : 0x401439aa  PS      : 0x00060a30  A0      : 0x800ef5d9  A1      : 0x3ffb21d0  
A2      : 0x3ffb223c  A3      : 0x3f7fcaa0  A4      : 0x00000000  A5      : 0x000000ad  
A6      : 0x00000001  A7      : 0x000000e0  A8      : 0x00000000  A9      : 0x00000126  
A10     : 0x000000e7  A11     : 0x00000002  A12     : 0x00000000  A13     : 0x0000ff00  
A14     : 0x00ff0000  A15     : 0xff000000  SAR     : 0x00000017  EXCCAUSE: 0x00000003  
EXCVADDR: 0x3f7fcaa0  LBEG    : 0x4008b53d  LEND    : 0x4008b54d  LCOUNT  : 0xffffffff  


Backtrace: 0x401439a7:0x3ffb21d0 0x400ef5d6:0x3ffb21f0 0x400d2f8e:0x3ffb2210 0x400d304b:0x3ffb2270 0x400d8b9d:0x3ffb2290




ELF file SHA256: fa15e686eec0fa6f

Rebooting...

Just remember, this happens in certain face positions (somehow moving my head up triggered the crash)

I think this has a tool to dissect it.

Why did you not show that crash data two days ago? Why did you not describe "face position" that crashes two days ago? Your goal on the forum, to resolve your issues, is to supply the forum with information, not withhold information. Without information, no one knows what you have.

How do I use that tool?

I found out this error occurs in my cat face detector too.

Does the "backtrace" decoder indicate the same error?

I literally asked how to use the tool in post #15

I litterally posted the link to the subject you refuse to read.

I did read it, but I don't understand this (I am a beginner so my knowledge is limited):
You can further use the addr2line command to parse the Backtrace into readable function names based on the .elf file. An example command is as follows:

xtensa-esp32s3-elf-addr2line -pfiaC -e path.elf 0x42016d2c:0x3fc98d00

Where do I place this command?