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,7 +16,7 @@ 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.
@ -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