Add Face Recognition functions and example

1. modify esp-face as a submodule
2. add documentation
3. improve detection performance
XiaochaoGONG 2018-11-02 20:20:25 +08:00
parent b30a6653e6
commit 83b8b990a7
14 changed files with 60 additions and 693 deletions

View File

@ -1,5 +1,6 @@
EXTRA_COMPONENT_DIRS += $(SOLUTION_PATH)/components
EXTRA_COMPONENT_DIRS += $(SOLUTION_PATH)/components/esp-face/lib
EXTRA_COMPONENT_DIRS += $(SOLUTION_PATH)/components/esp-face/image_util
EXTRA_COMPONENT_DIRS += $(SOLUTION_PATH)/components/esp-face/face_detection/fd_coefficients
EXTRA_COMPONENT_DIRS += $(SOLUTION_PATH)/components/esp-face/face_detection/mtmn
EXTRA_COMPONENT_DIRS += $(SOLUTION_PATH)/components/esp-face/face_recognition/fr_coefficients

@ -1 +1 @@
Subproject commit 84acf59b23afafafc5c72f2e31dddbb54d56aaa1
Subproject commit 4ed00b5f61bb59b54ff56fba14597eed87c7b36a

View File

@ -1,404 +0,0 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include <stdbool.h>
#include <string.h>
#include "dl_lib.h"
#include "image_util.h"
#include "freertos/FreeRTOS.h"
void image_resize_linear(uint8_t *dst_image, uint8_t *src_image, int dst_w, int dst_h, int dst_c, int src_w, int src_h)
{ /*{{{*/
float scale_x = (float)src_w / dst_w;
float scale_y = (float)src_h / dst_h;
int dst_stride = dst_c * dst_w;
int src_stride = dst_c * src_w;
for (int y = 0; y < dst_h; y++)
{
float fy[2];
fy[0] = (float)((y + 0.5) * scale_y - 0.5); // y
int src_y = (int)fy[0]; // y1
fy[0] -= src_y; // y - y1
fy[1] = 1 - fy[0]; // y2 - y
src_y = DL_IMAGE_MAX(0, src_y);
src_y = DL_IMAGE_MIN(src_y, src_h - 2);
for (int x = 0; x < dst_w; x++)
{
float fx[2];
fx[0] = (float)((x + 0.5) * scale_x - 0.5); // x
int src_x = (int)fx[0]; // x1
fx[0] -= src_x; // x - x1
if (src_x < 0)
{
fx[0] = 0;
src_x = 0;
}
if (src_x > src_w - 2)
{
fx[0] = 0;
src_x = src_w - 2;
}
fx[1] = 1 - fx[0]; // x2 - x
for (int c = 0; c < dst_c; c++)
{
dst_image[y * dst_stride + x * dst_c + c] = round(src_image[src_y * src_stride + src_x * dst_c + c] * fx[1] * fy[1] + src_image[src_y * src_stride + (src_x + 1) * dst_c + c] * fx[0] * fy[1] + src_image[(src_y + 1) * src_stride + src_x * dst_c + c] * fx[1] * fy[0] + src_image[(src_y + 1) * src_stride + (src_x + 1) * dst_c + c] * fx[0] * fy[0]);
}
}
}
} /*}}}*/
void image_cropper(dl_matrix3du_t *corp_image, dl_matrix3du_t *src_image, float rotate_angle, float ratio, float *center)
{
int rot_w = (int)corp_image->w;
int rot_h = (int)corp_image->h;
int rot_c = src_image->c;
int rot_stride = rot_w * rot_c;
uint8_t *rot_data = corp_image->item;
float rot_w_start = 0.5f - (float)rot_w / 2;
float rot_h_start = 0.5f - (float)rot_h / 2;
//rotate_angle must be radius
float si = sin(rotate_angle);
float co = cos(rotate_angle);
uint8_t *src_data = src_image->item;
int src_w = src_image->w;
int src_h = src_image->h;
int src_c = src_image->c;
int src_stride = src_image->stride;
for (int y = 0; y < rot_h; y++)
{
for (int x = 0; x < rot_w; x++)
{
float xs, ys, xr, yr;
xs = ratio * (rot_w_start + x);
ys = ratio * (rot_h_start + y);
xr = xs * co + ys * si;
yr = -xs * si + ys * co;
float fy[2];
fy[0] = center[1] + yr; // y
int src_y = (int)fy[0]; // y1
fy[0] -= src_y; // y - y1
fy[1] = 1 - fy[0]; // y2 - y
src_y = DL_IMAGE_MAX(0, src_y);
src_y = DL_IMAGE_MIN(src_y, src_h - 2);
float fx[2];
fx[0] = center[0] + xr; // x
int src_x = (int)fx[0]; // x1
fx[0] -= src_x; // x - x1
if (src_x < 0)
{
fx[0] = 0;
src_x = 0;
}
if (src_x > src_w - 2)
{
fx[0] = 0;
src_x = src_w - 2;
}
fx[1] = 1 - fx[0]; // x2 - x
for (int c = 0; c < rot_c; c++)
{
rot_data[y * rot_stride + x * rot_c + c] = round(src_data[src_y * src_stride + src_x * src_c + c] * fx[1] * fy[1] + src_data[src_y * src_stride + (src_x + 1) * src_c + c] * fx[0] * fy[1] + src_data[(src_y + 1) * src_stride + src_x * src_c + c] * fx[1] * fy[0] + src_data[(src_y + 1) * src_stride + (src_x + 1) * src_c + c] * fx[0] * fy[0]);
}
}
}
}
void image_sort_insert_by_score(image_list_t *image_sorted_list, const image_list_t *insert_list)
{ /*{{{*/
if (insert_list == NULL || insert_list->head == NULL)
return;
image_box_t *box = insert_list->head;
if (NULL == image_sorted_list->head)
{
image_sorted_list->head = insert_list->head;
box = insert_list->head->next;
image_sorted_list->head->next = NULL;
}
image_box_t *head = image_sorted_list->head;
while (box)
{
// insert in head
if (box->score > head->score)
{
image_box_t *tmp = box;
box = box->next;
tmp->next = head;
head = tmp;
}
else
{
image_box_t *curr = head->next;
image_box_t *prev = head;
while (curr)
{
if (box->score > curr->score)
{
image_box_t *tmp = box;
box = box->next;
tmp->next = curr;
prev->next = tmp;
break;
}
prev = curr;
curr = curr->next;
}
// insert in tail
if (NULL == curr)
{
image_box_t *tmp = box;
box = box->next;
tmp->next = NULL;
prev->next = tmp;
}
}
}
image_sorted_list->head = head;
image_sorted_list->len += insert_list->len;
} /*}}}*/
image_list_t *image_get_valid_boxes(fptp_t *score, fptp_t *offset, int width, int height, fptp_t score_threshold, fptp_t scale)
{ /*{{{*/
typedef struct
{
short valid_x;
short valid_y;
int valid_idx;
} valid_index_t;
valid_index_t *valid_indexes = (valid_index_t *)calloc(width * height, sizeof(valid_index_t));
int valid_count = 0;
int index = 0;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
if (score[2 * index + 1] > score_threshold)
{
valid_indexes[valid_count].valid_x = x;
valid_indexes[valid_count].valid_y = y;
valid_indexes[valid_count].valid_idx = index;
valid_count++;
}
index++;
}
if (0 == valid_count)
{
free(valid_indexes);
return NULL;
}
image_box_t *valid_box = (image_box_t *)calloc(valid_count, sizeof(image_box_t));
image_list_t *valid_list = (image_list_t *)calloc(1, sizeof(image_list_t));
valid_list->head = valid_box;
valid_list->origin_head = valid_box;
valid_list->len = valid_count;
for (int i = 0; i < valid_count; i++)
{
fptp_t x1 = valid_indexes[i].valid_x * scale * 2.0;
fptp_t y1 = valid_indexes[i].valid_y * scale * 2.0;
int valid_i = valid_indexes[i].valid_idx;
valid_box[i].score = score[2 * valid_i + 1];
valid_box[i].box.box_p[0] = x1;
valid_box[i].box.box_p[1] = y1;
valid_box[i].box.box_p[2] = x1 + MIN_FACE * scale;
valid_box[i].box.box_p[3] = y1 + MIN_FACE * scale;
valid_box[i].offset.box_p[0] = offset[valid_i * 4 + 0];
valid_box[i].offset.box_p[1] = offset[valid_i * 4 + 1];
valid_box[i].offset.box_p[2] = offset[valid_i * 4 + 2];
valid_box[i].offset.box_p[3] = offset[valid_i * 4 + 3];
valid_box[i].next = &(valid_box[i + 1]);
}
valid_box[valid_count - 1].next = NULL;
free(valid_indexes);
return valid_list;
} /*}}}*/
void image_nms_process(image_list_t *image_list, fptp_t nms_threshold, int same_area)
{ /*{{{*/
/**** Init ****/
int num_supressed = 0;
image_box_t *head = image_list->head;
/**** Compute Box Area ****/
fptp_t kept_box_area = 0;
fptp_t other_box_area = 0;
if (same_area)
{
image_get_area(&(head->box), &kept_box_area);
other_box_area = kept_box_area;
}
/**** Compare IOU ****/
image_box_t *kept_box = head;
while (kept_box)
{
image_box_t *other_box = kept_box->next;
image_box_t *prev = kept_box;
while (other_box)
{
// kept_box is contained in other_box
if (((kept_box->box.box_p[0] > other_box->box.box_p[0]) && (kept_box->box.box_p[1] > other_box->box.box_p[1]) && (kept_box->box.box_p[2] < other_box->box.box_p[2]) && (kept_box->box.box_p[3] < other_box->box.box_p[3]))
// kept_box contains other_box
|| ((kept_box->box.box_p[0] < other_box->box.box_p[0]) && (kept_box->box.box_p[1] < other_box->box.box_p[1]) && (kept_box->box.box_p[2] > other_box->box.box_p[2]) && (kept_box->box.box_p[3] > other_box->box.box_p[3])))
// supress
{
num_supressed++;
prev->next = other_box->next;
other_box = other_box->next;
continue;
}
box_t inter_box;
inter_box.box_p[0] = DL_IMAGE_MAX(kept_box->box.box_p[0], other_box->box.box_p[0]);
inter_box.box_p[1] = DL_IMAGE_MAX(kept_box->box.box_p[1], other_box->box.box_p[1]);
inter_box.box_p[2] = DL_IMAGE_MIN(kept_box->box.box_p[2], other_box->box.box_p[2]);
inter_box.box_p[3] = DL_IMAGE_MIN(kept_box->box.box_p[3], other_box->box.box_p[3]);
fptp_t inter_w, inter_h;
image_get_width_and_height(&inter_box, &inter_w, &inter_h);
if (inter_w > 0 && inter_h > 0)
{
if (!same_area)
{
image_get_area(&(kept_box->box), &kept_box_area);
image_get_area(&(other_box->box), &other_box_area);
}
fptp_t inter_area = inter_w * inter_h;
fptp_t iou = inter_area / (kept_box_area + other_box_area - inter_area);
if (iou > nms_threshold)
{
num_supressed++;
// Delete duplicated box
// Here we cannot free a single box, because these boxes are allocated by calloc, we need to free all the calloced memory together.
prev->next = other_box->next;
other_box = other_box->next;
continue;
}
}
prev = other_box;
other_box = other_box->next;
}
kept_box = kept_box->next;
}
image_list->len -= num_supressed;
} /*}}}*/
void transform_input_image(uint8_t *m, uint16_t *bmp, int count)
{ /*{{{*/
uc_t dst[24];
for (int x = 0; x < count; x += 8)
{
rgb565_to_888(*bmp++, dst);
rgb565_to_888(*bmp++, dst + 3);
rgb565_to_888(*bmp++, dst + 6);
rgb565_to_888(*bmp++, dst + 9);
rgb565_to_888(*bmp++, dst + 12);
rgb565_to_888(*bmp++, dst + 15);
rgb565_to_888(*bmp++, dst + 18);
rgb565_to_888(*bmp++, dst + 21);
memcpy(m + x * 3, dst, 24 * sizeof(uint8_t));
}
} /*}}}*/
void transform_output_image(uint16_t *bmp, uint8_t *m, int count)
{ /*{{{*/
for (int x = 0; x < count; x++)
{
rgb888_to_565(bmp, m[2], m[1], m[0]);
bmp++;
m += 3;
}
} /*}}}*/
void draw_rectangle(uint16_t *buf, box_array_t *boxes, int width)
{ /*{{{*/
uint16_t p[14];
for (int i = 0; i < boxes->len; i++)
{
// rectangle box
for (int j = 0; j < 4; j++)
p[j] = (uint16_t)boxes->box[i].box_p[j];
// landmark
for (int j = 0; j < 10; j++)
p[j + 4] = (uint16_t)boxes->landmark[i].landmark_p[j];
if ((p[2] < p[0]) || (p[3] < p[1]))
return;
#define GREEN RGB565_MASK_GREEN
#define RED RGB565_MASK_GREEN
// rectangle box
for (int w = p[0]; w < p[2] + 1; w++)
{
int x1 = (p[1] * width + w);
int x2 = (p[3] * width + w);
buf[x1] = GREEN; // Green
buf[x2] = GREEN;
}
for (int h = p[1]; h < p[3] + 1; h++)
{
int y1 = (h * width + p[0]);
int y2 = (h * width + p[2]);
buf[y1] = GREEN; // Green
buf[y2] = GREEN;
}
// landmark
for (int j = 0; j < 10; j += 2)
{
int x = p[j + 5] * width + p[j + 4];
buf[x] = RED; // Red
buf[x + 1] = RED;
buf[x + 2] = RED;
buf[width + x] = RED; // Red
buf[width + x + 1] = RED;
buf[width + x + 2] = RED;
buf[2 * width + x] = RED; // Red
buf[2 * width + x + 1] = RED;
buf[2 * width + x + 2] = RED;
}
}
} /*}}}*/

View File

@ -1,262 +0,0 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include "mtmn.h"
#define BOX_LEN (80)
#define MIN_FACE (12.0)
#define MAX_VALID_COUNT_PER_IMAGE (30)
#define DL_IMAGE_MIN(A, B) ((A) < (B) ? (A) : (B))
#define DL_IMAGE_MAX(A, B) ((A) < (B) ? (B) : (A))
#define IMAGE_WIDTH 320
#define IMAGE_HEIGHT 240
#define RGB565_MASK_RED 0xF800
#define RGB565_MASK_GREEN 0x07E0
#define RGB565_MASK_BLUE 0x001F
typedef struct
{
fptp_t landmark_p[10];
} landmark_t;
typedef struct
{
fptp_t box_p[4];
} box_t;
typedef struct tag_box_list
{
box_t *box;
landmark_t *landmark;
int len;
} box_array_t;
typedef struct tag_image_box
{
struct tag_image_box *next;
fptp_t score;
box_t box;
box_t offset;
landmark_t landmark;
} image_box_t;
typedef struct tag_image_list
{
image_box_t *head;
image_box_t *origin_head;
int len;
} image_list_t;
static inline void image_get_width_and_height(box_t *box, float *w, float *h)
{
*w = box->box_p[2] - box->box_p[0] + 1;
*h = box->box_p[3] - box->box_p[1] + 1;
}
static inline void image_get_area(box_t *box, float *area)
{
float w, h;
image_get_width_and_height(box, &w, &h);
*area = w * h;
}
static inline void image_calibrate_by_offset(image_list_t *image_list)
{
for (image_box_t *head = image_list->head; head; head = head->next)
{
float w, h;
image_get_width_and_height(&(head->box), &w, &h);
head->box.box_p[0] = DL_IMAGE_MAX(0, head->box.box_p[0] + head->offset.box_p[0] * w);
head->box.box_p[1] = DL_IMAGE_MAX(0, head->box.box_p[1] + head->offset.box_p[1] * w);
head->box.box_p[2] += head->offset.box_p[2] * w;
if (head->box.box_p[2] > IMAGE_WIDTH)
{
head->box.box_p[2] = IMAGE_WIDTH - 1;
head->box.box_p[0] = IMAGE_WIDTH - w;
}
head->box.box_p[3] += head->offset.box_p[3] * h;
if (head->box.box_p[3] > IMAGE_HEIGHT)
{
head->box.box_p[3] = IMAGE_HEIGHT - 1;
head->box.box_p[1] = IMAGE_HEIGHT - h;
}
}
}
static inline void image_landmark_calibrate(image_list_t *image_list)
{
for (image_box_t *head = image_list->head; head; head = head->next)
{
float w, h;
image_get_width_and_height(&(head->box), &w, &h);
head->landmark.landmark_p[0] = head->box.box_p[0] + head->landmark.landmark_p[0] * w;
head->landmark.landmark_p[1] = head->box.box_p[1] + head->landmark.landmark_p[1] * h;
head->landmark.landmark_p[2] = head->box.box_p[0] + head->landmark.landmark_p[2] * w;
head->landmark.landmark_p[3] = head->box.box_p[1] + head->landmark.landmark_p[3] * h;
head->landmark.landmark_p[4] = head->box.box_p[0] + head->landmark.landmark_p[4] * w;
head->landmark.landmark_p[5] = head->box.box_p[1] + head->landmark.landmark_p[5] * h;
head->landmark.landmark_p[6] = head->box.box_p[0] + head->landmark.landmark_p[6] * w;
head->landmark.landmark_p[7] = head->box.box_p[1] + head->landmark.landmark_p[7] * h;
head->landmark.landmark_p[8] = head->box.box_p[0] + head->landmark.landmark_p[8] * w;
head->landmark.landmark_p[9] = head->box.box_p[1] + head->landmark.landmark_p[9] * h;
}
}
static inline void image_rect2sqr(box_array_t *boxes, int width, int height)
{
for (int i = 0; i < boxes->len; i++)
{
box_t *box = &(boxes->box[i]);
float w, h;
image_get_width_and_height(box, &w, &h);
float l = DL_IMAGE_MAX(w, h);
box->box_p[0] = DL_IMAGE_MAX(0, box->box_p[0] + 0.5 * (w - l));
box->box_p[1] = DL_IMAGE_MAX(0, box->box_p[1] + 0.5 * (h - l));
box->box_p[2] = box->box_p[0] + l - 1;
if (box->box_p[2] > width)
{
box->box_p[2] = width - 1;
box->box_p[0] = width - l;
}
box->box_p[3] = box->box_p[1] + l - 1;
if (box->box_p[3] > height)
{
box->box_p[3] = height - 1;
box->box_p[1] = height - l;
}
}
}
static inline void rgb565_to_888(uint16_t in, uint8_t *dst)
{ /*{{{*/
dst[0] = (in & RGB565_MASK_BLUE) << 3; // blue
dst[1] = (in & RGB565_MASK_GREEN) >> 3; // green
dst[2] = (in & RGB565_MASK_RED) >> 8; // red
} /*}}}*/
static inline void rgb888_to_565(uint16_t *in, uint8_t r, uint8_t g, uint8_t b)
{ /*{{{*/
uint16_t rgb565 = 0;
rgb565 = ((r >> 3) << 11);
rgb565 |= ((g >> 2) << 5);
rgb565 |= (b >> 3);
*in = rgb565;
} /*}}}*/
/**
* @brief
*
* @param score
* @param offset
* @param width
* @param height
* @param score_threshold
* @param scale
* @return image_list_t*
*/
image_list_t *image_get_valid_boxes(fptp_t *score, fptp_t *offset, int width, int height, fptp_t score_threshold, fptp_t scale);
/**
* @brief
*
* @param image_sorted_list
* @param insert_list
*/
void image_sort_insert_by_score(image_list_t *image_sorted_list, const image_list_t *insert_list);
/**
* @brief
*
* @param image_list
* @param nms_threshold
* @param same_area
*/
void image_nms_process(image_list_t *image_list, fptp_t nms_threshold, int same_area);
/**
* @brief
*
* @param dst_image
* @param src_image
* @param dst_w
* @param dst_h
* @param dst_c
* @param src_w
* @param src_h
*/
void image_resize_linear(uint8_t *dst_image, uint8_t *src_image, int dst_w, int dst_h, int dst_c, int src_w, int src_h);
/**
* @brief
*
* @param corp_image
* @param src_image
* @param rotate_angle
* @param ratio
* @param center
*/
void image_cropper(dl_matrix3du_t *corp_image, dl_matrix3du_t *src_image, float rotate_angle, float ratio, float *center);
/**
* @brief
*
* @param m
* @param bmp
* @param count
*/
void transform_input_image(uint8_t *m, uint16_t *bmp, int count);
/**
* @brief
*
* @param bmp
* @param m
* @param count
*/
void transform_output_image(uint16_t *bmp, uint8_t *m, int count);
/**
* @brief
*
* @param buf
* @param boxes
* @param width
*/
void draw_rectangle(uint16_t *buf, box_array_t *boxes, int width);
#ifdef __cplusplus
}
#endif

View File

@ -1,6 +1,6 @@
# Camera with Command Line in Single Chip
# Recognition with Command Line in Single Chip
This example implements Human Face Detection with a single ESP32 chip and without LCD. ESP32 gets input of image from camera and displays results in the Command Line after recognition.
This example implements Human Face Recognition with a single ESP32 chip and without LCD. ESP32 gets input of image from camera and displays results in the Command Line after recognition.
# Preparation
@ -16,31 +16,31 @@ Any other confusions about preparation, please see general guide in the README.m
# Quick Start
If preparations are ready, please follow this section to **connect** the camera to ESP32 module, flash application to ESP32, finally execute human face detection and display the **result**.
If preparations are ready, please follow this section to **connect** the camera to ESP32 module, flash application to ESP32, finally execute human face recognition and display the **result**.
## Connect
Specific pins used in this example to connect ESP32 module and camera module are listed in table below.
Specific pins used in this example to connect ESP32 module and camera module are listed in table below.
| Interface | Camera Pin | Pin Mapping for ESP32-WROVER |
| Interface | Camera Pin | Pin Mapping for ESP32-WROVER |
| :--- | :---: | :---: |
| SCCB Clock | SIOC | IO27 |
| SCCB Data | SIOD | IO26 |
| System Clock | XCLK | IO21 |
| Vertical Sync | VSYNC | IO25 |
| Horizontal Reference | HREF | IO23 |
| Pixel Clock | PCLK | IO22 |
| Pixel Data Bit 0 | D2 | IO4 |
| Pixel Data Bit 1 | D3 | IO5 |
| Pixel Data Bit 2 | D4 | IO18 |
| Pixel Data Bit 3 | D5 | IO19 |
| Pixel Data Bit 4 | D6 | IO36 |
| Pixel Data Bit 5 | D7 | IO39 |
| Pixel Data Bit 6 | D8 | IO34 |
| Pixel Data Bit 7 | D9 | IO35 |
| Camera Reset | RESET | IO2 |
| Camera Power Down | PWDN | IO0 |
| Power Supply 3.3V | 3V3 | 3V3 |
| Ground | GND | GND |
| SCCB Data | SIOD | IO26 |
| System Clock | XCLK | IO21 |
| Vertical Sync | VSYNC | IO25 |
| Horizontal Reference | HREF | IO23 |
| Pixel Clock | PCLK | IO22 |
| Pixel Data Bit 0 | D2 | IO4 |
| Pixel Data Bit 1 | D3 | IO5 |
| Pixel Data Bit 2 | D4 | IO18 |
| Pixel Data Bit 3 | D5 | IO19 |
| Pixel Data Bit 4 | D6 | IO36 |
| Pixel Data Bit 5 | D7 | IO39 |
| Pixel Data Bit 6 | D8 | IO34 |
| Pixel Data Bit 7 | D9 | IO35 |
| Camera Reset | RESET | IO2 |
| Camera Power Down | PWDN | IO0 |
| Power Supply 3.3V | 3V3 | 3V3 |
| Ground | GND | GND |
In particular, if you have a **ESP-WROVER-KIT**, camera connector is already broken out and labeled Camera / JP4. Solder 2.54 mm / 0.1" double row, 18 pin socket in provided space and plug the camera module, OV2640 for example, right into it. Line up 3V3 and GND pins on camera module and on ESP-WROVER-KIT. D0 and D1 should be left unconnected outside the socket. The image below shows **ESP-WROVER-KIT** plugged with **OV2640** camera module.
@ -50,9 +50,28 @@ In particular, if you have a **ESP-WROVER-KIT**, camera connector is already bro
## Results
Open a serial terminal by using `make monitor` at this project, point the camera to a human face with a distance of 0.3m at least, then you will see the following information:
Open a serial terminal by using `make monitor` at this project, point the camera to a human face with a distance of 0.3m at least, then face entry will start after two faces are detected. The following information shows the output of the command line before the entry process:
![detected](../../../img/detected.png)
![login_delay2](../../../img/login_delay2.png)
![login_delay1](../../../img/login_delay1.png)
The key word **DETECTED** comes out when a human face is detected.
#### Login
Then the face entry process will begin, during which the program will input multiple faces to get a person's ID:
![start_login](../../../img/start_login.png)
You can also reset the number of faces entered by one person, which is set to 3 by default.
When the last face of ID is entered, the process of face recognition will start:
![start_recognition](../../../img/start_recognition.png)
#### Recognition
When a face is detected, it will be recognized whether the face is the same as the entered ID. If it is the same, the corresponding ID number will be displayed:
![recognition_matched](../../../img/matched.png)
Otherwise, the command line will display `No Matched ID`:
![recognition_no_matched](../../../img/no_matched.png)

View File

@ -68,6 +68,8 @@ void task_process(void *arg)
int is_logging = 1;
int next_logging_index = 0;
int64_t timestamp = 0;
do
{
img_buffer = (uint16_t *)facenet_get_image();
@ -75,7 +77,9 @@ void task_process(void *arg)
img_buffer,
gl_input_image_width * gl_input_image_height);
timestamp = esp_timer_get_time();
box_array_t *net_boxes = face_detect(image_matrix);
ESP_LOGI(TAG, "Detection time consumption: %lldms", (esp_timer_get_time() - timestamp) / 1000);
if (net_boxes)
{
@ -129,10 +133,19 @@ void task_process(void *arg)
}
else
{
timestamp = esp_timer_get_time();
int matched_id = recognize_face(aligned_face,
id_list, thresh,
next_logging_index);
ESP_LOGE(TAG, "Matched ID: %d", matched_id);
if (matched_id)
{
ESP_LOGE(TAG, "Matched ID: %d", matched_id);
}
else
{
ESP_LOGE(TAG, "No Matched ID");
}
ESP_LOGI(TAG, "Recognition time consumption: %lldms", (esp_timer_get_time() - timestamp) / 1000);
}
}
else

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

BIN
img/login_delay1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
img/login_delay2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
img/matched.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
img/no_matched.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
img/start_login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
img/start_recognition.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB