From 83b414dd0d369eabec95dbf4a0b2a0ce94e1ab49 Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Wed, 20 Oct 2021 20:12:08 +0800 Subject: [PATCH 01/12] :sparkles: add gpio buttons and adc buttons --- components/modules/button/who_adc_button.c | 110 +++++++++++++++++++++ components/modules/button/who_adc_button.h | 30 ++++++ components/modules/button/who_button.c | 100 +++++++++++++++++++ components/modules/button/who_button.h | 29 ++++++ 4 files changed, 269 insertions(+) create mode 100644 components/modules/button/who_adc_button.c create mode 100644 components/modules/button/who_adc_button.h create mode 100644 components/modules/button/who_button.c create mode 100644 components/modules/button/who_button.h diff --git a/components/modules/button/who_adc_button.c b/components/modules/button/who_adc_button.c new file mode 100644 index 0000000..59a0a41 --- /dev/null +++ b/components/modules/button/who_adc_button.c @@ -0,0 +1,110 @@ +/* ADC1 Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/adc_common.h" +#include "esp_adc_cal.h" +#include "who_adc_button.h" + +//ADC Channels +#define ADC1_EXAMPLE_CHAN0 ADC1_CHANNEL_0 +//ADC Attenuation +#define ADC_EXAMPLE_ATTEN ADC_ATTEN_DB_11 +//ADC Calibration +#if CONFIG_IDF_TARGET_ESP32 +#define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_VREF +#elif CONFIG_IDF_TARGET_ESP32S2 +#define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP +#elif CONFIG_IDF_TARGET_ESP32C3 +#define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP +#elif CONFIG_IDF_TARGET_ESP32S3 +#define ADC_EXAMPLE_CALI_SCHEME ESP_ADC_CAL_VAL_EFUSE_TP_FIT +#endif + +#define PRESS_INTERVAL 500000 + +static uint32_t voltage = 0; + +static const char *TAG = "ADC SINGLE"; + +static esp_adc_cal_characteristics_t adc1_chars; + +button_adc_config_t *adc_buttons; +int adc_button_num; +static QueueHandle_t xQueueKeyStateO = NULL; + +static bool adc_calibration_init(void) +{ + esp_err_t ret; + bool cali_enable = false; + + ret = esp_adc_cal_check_efuse(ADC_EXAMPLE_CALI_SCHEME); + if (ret == ESP_ERR_NOT_SUPPORTED) + { + ESP_LOGW(TAG, "Calibration scheme not supported, skip software calibration"); + } + else if (ret == ESP_ERR_INVALID_VERSION) + { + ESP_LOGW(TAG, "eFuse not burnt, skip software calibration"); + } + else if (ret == ESP_OK) + { + cali_enable = true; + esp_adc_cal_characterize(ADC_UNIT_1, ADC_EXAMPLE_ATTEN, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars); + } + else + { + ESP_LOGE(TAG, "Invalid arg"); + } + return cali_enable; +} + +void adc_button_task(void *arg) +{ + int last_button_pressed = -1; + int button_pressed = -1; + int64_t backup_time = esp_timer_get_time(); + int64_t last_time = esp_timer_get_time(); + + //ADC1 config + ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_DEFAULT)); + ESP_ERROR_CHECK(adc1_config_channel_atten(ADC1_EXAMPLE_CHAN0, ADC_EXAMPLE_ATTEN)); + + while (1) + { + voltage = adc1_get_raw(ADC1_EXAMPLE_CHAN0); + backup_time = esp_timer_get_time(); + for (int i = 0; i < adc_button_num; ++i) + { + if ((voltage >= adc_buttons[i].min) && (voltage <= adc_buttons[i].max)) + { + button_pressed = adc_buttons[i].button_index; + if ((button_pressed != last_button_pressed) || ((backup_time - last_time) > PRESS_INTERVAL)) + { + last_button_pressed = button_pressed; + last_time = backup_time; + xQueueOverwrite(xQueueKeyStateO, &button_pressed); + break; + } + } + } + vTaskDelay(pdMS_TO_TICKS(10)); + } +} + +void register_adc_button(button_adc_config_t *buttons_ptr, int button_num, const QueueHandle_t key_state_o) +{ + xQueueKeyStateO = key_state_o; + adc_buttons = buttons_ptr; + adc_button_num = button_num; + xTaskCreatePinnedToCore(adc_button_task, "adc_button_scan_task", 1024, NULL, 5, NULL, 0); +} \ No newline at end of file diff --git a/components/modules/button/who_adc_button.h b/components/modules/button/who_adc_button.h new file mode 100644 index 0000000..25fb0af --- /dev/null +++ b/components/modules/button/who_adc_button.h @@ -0,0 +1,30 @@ +#pragma once +#include "esp_event_loop.h" +#include "soc/system_reg.h" +#include "driver/gpio.h" +#include "esp_log.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + int button_index; /**< button index on the channel */ + int min; /**< min voltage in mv corresponding to the button */ + int max; /**< max voltage in mv corresponding to the button */ + } button_adc_config_t; + + /** + * @brief initialize adc button + * + * @param buttons_ptr the pointer of adc button configuration + * @param button_num the numbers of adc buttons + * @param key_state_o the queue to send which button is pressed + */ + void register_adc_button(button_adc_config_t *buttons_ptr, int button_num, const QueueHandle_t key_state_o); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/modules/button/who_button.c b/components/modules/button/who_button.c new file mode 100644 index 0000000..1bf7f9f --- /dev/null +++ b/components/modules/button/who_button.c @@ -0,0 +1,100 @@ +#include +#include +#include "who_button.h" + +typedef struct +{ + gpio_num_t io_num; + key_state_t state; +} key_scan_state_t; + +#define LONG_PRESS_THRESH 700000 +#define DOUBLE_CLICK_THRESH 300000 + +static xQueueHandle gpio_evt_queue = NULL; +static QueueHandle_t xQueueKeyStateO = NULL; + +static void IRAM_ATTR gpio_isr_handler_key(void *arg) +{ + uint32_t gpio_num = (uint32_t)arg; + xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); +} + +int key_scan(TickType_t ticks_to_wait) +{ + gpio_num_t io_num; + BaseType_t press_key = pdFALSE; + BaseType_t lift_key = pdFALSE; + int64_t backup_time = 0; + int64_t interval_time = 0; + static int64_t last_time = 0; + + for (;;) + { + xQueueReceive(gpio_evt_queue, &io_num, ticks_to_wait); + + if (gpio_get_level(io_num) == 0) + { + press_key = pdTRUE; + backup_time = esp_timer_get_time(); + interval_time = backup_time - last_time; + } + else if (press_key) + { + lift_key = pdTRUE; + last_time = esp_timer_get_time(); + backup_time = last_time - backup_time; + } + + if (press_key & lift_key) + { + press_key = pdFALSE; + lift_key = pdFALSE; + + if (backup_time > LONG_PRESS_THRESH) + { + return KEY_LONG_PRESS; + } + else + { + if ((interval_time < DOUBLE_CLICK_THRESH) && (interval_time > 0)) + return KEY_DOUBLE_CLICK; + else + return KEY_SHORT_PRESS; + } + } + } +} + +void key_trigger(void *arg) +{ + int ret = 0; + + while (1) + { + ret = key_scan(portMAX_DELAY); + xQueueOverwrite(xQueueKeyStateO, &ret); + } + + vTaskDelete(NULL); +} + +void key_init(gpio_num_t gpio_num) +{ + gpio_config_t io_conf = {0}; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.intr_type = GPIO_INTR_ANYEDGE; + io_conf.pin_bit_mask = 1LL << gpio_num; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_config(&io_conf); + gpio_evt_queue = xQueueCreate(5, sizeof(uint32_t)); + gpio_install_isr_service(0); + gpio_isr_handler_add(gpio_num, gpio_isr_handler_key, (void *)gpio_num); +} + +void register_button(const gpio_num_t key_io_num, const QueueHandle_t key_state_o) +{ + xQueueKeyStateO = key_state_o; + key_init(key_io_num); + xTaskCreatePinnedToCore(key_trigger, "key_scan_task", 1024, NULL, 5, NULL, 0); +} \ No newline at end of file diff --git a/components/modules/button/who_button.h b/components/modules/button/who_button.h new file mode 100644 index 0000000..e5989c0 --- /dev/null +++ b/components/modules/button/who_button.h @@ -0,0 +1,29 @@ +#pragma once +#include "esp_event_loop.h" +#include "soc/system_reg.h" +#include "driver/gpio.h" +#include "esp_log.h" + +typedef enum +{ + KEY_SHORT_PRESS = 1, + KEY_LONG_PRESS, + KEY_DOUBLE_CLICK, +} key_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief initialize gpio button + * + * @param key_io_num the gpio number of the button + * @param key_state_o the queue to send the button state + */ +void register_button(const gpio_num_t key_io_num, const QueueHandle_t key_state_o); + +#ifdef __cplusplus +} +#endif \ No newline at end of file From ecd5785d02dd94a1fe5ce84086efe8feb561903c Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Wed, 20 Oct 2021 20:15:03 +0800 Subject: [PATCH 02/12] :sparkles: feature: face recognition --- .../modules/ai/who_human_face_recognition.cpp | 239 ++++++++++++++++++ .../modules/ai/who_human_face_recognition.hpp | 21 ++ 2 files changed, 260 insertions(+) create mode 100644 components/modules/ai/who_human_face_recognition.cpp create mode 100644 components/modules/ai/who_human_face_recognition.hpp diff --git a/components/modules/ai/who_human_face_recognition.cpp b/components/modules/ai/who_human_face_recognition.cpp new file mode 100644 index 0000000..c416e73 --- /dev/null +++ b/components/modules/ai/who_human_face_recognition.cpp @@ -0,0 +1,239 @@ +#include "who_human_face_recognition.hpp" + +#include "esp_log.h" +#include "esp_camera.h" + +#include "dl_image.hpp" +#include "fb_gfx.h" + +#include "human_face_detect_msr01.hpp" +#include "human_face_detect_mnp01.hpp" +#include "face_recognition_tool.hpp" + +#if CONFIG_MFN_V1 +#if CONFIG_S8 +#include "face_recognition_112_v1_s8.hpp" +#elif CONFIG_S16 +#include "face_recognition_112_v1_s16.hpp" +#endif +#endif + +#include "who_ai_utils.hpp" + +using namespace std; +using namespace dl; + +static const char *TAG = "human_face_detection"; + +static QueueHandle_t xQueueFrameI = NULL; +static QueueHandle_t xQueueEvent = NULL; +static QueueHandle_t xQueueFrameO = NULL; +static QueueHandle_t xQueueResult = NULL; + +static recognizer_state_t gEvent = DETECT; +static bool gReturnFB = true; +static face_info_t recognize_result; + +SemaphoreHandle_t xMutex; + +typedef enum +{ + SHOW_STATE_IDLE, + SHOW_STATE_DELETE, + SHOW_STATE_RECOGNIZE, + SHOW_STATE_ENROLL, +} show_state_t; + +#define RGB565_MASK_RED 0xF800 +#define RGB565_MASK_GREEN 0x07E0 +#define RGB565_MASK_BLUE 0x001F +#define FRAME_DELAY_NUM 16 + +static void rgb_print(camera_fb_t *fb, uint32_t color, const char *str) +{ + fb_gfx_print(fb, (fb->width - (strlen(str) * 14)) / 2, 10, color, str); +} + +static int rgb_printf(camera_fb_t *fb, uint32_t color, const char *format, ...) +{ + char loc_buf[64]; + char *temp = loc_buf; + int len; + va_list arg; + va_list copy; + va_start(arg, format); + va_copy(copy, arg); + len = vsnprintf(loc_buf, sizeof(loc_buf), format, arg); + va_end(copy); + if (len >= sizeof(loc_buf)) + { + temp = (char *)malloc(len + 1); + if (temp == NULL) + { + return 0; + } + } + vsnprintf(temp, len + 1, format, arg); + va_end(arg); + rgb_print(fb, color, temp); + if (len > 64) + { + free(temp); + } + return len; +} + +static void task_process_handler(void *arg) +{ + camera_fb_t *frame = NULL; + HumanFaceDetectMSR01 detector(0.3F, 0.3F, 10, 0.3F); + HumanFaceDetectMNP01 detector2(0.4F, 0.3F, 10); + +#if CONFIG_MFN_V1 +#if CONFIG_S8 + FaceRecognition112V1S8 *recognizer = new FaceRecognition112V1S8(); +#elif CONFIG_S16 + FaceRecognition112V1S16 *recognizer = new FaceRecognition112V1S16(); +#endif +#endif + show_state_t frame_show_state = SHOW_STATE_IDLE; + recognizer_state_t _gEvent; + + while (true) + { + xSemaphoreTake(xMutex, portMAX_DELAY); + _gEvent = gEvent; + gEvent = DETECT; + xSemaphoreGive(xMutex); + + if (_gEvent) + { + bool is_detected = false; + + if (xQueueReceive(xQueueFrameI, &frame, portMAX_DELAY)) + { + std::list &detect_candidates = detector.infer((uint16_t *)frame->buf, {(int)frame->height, (int)frame->width, 3}); + std::list &detect_results = detector2.infer((uint16_t *)frame->buf, {(int)frame->height, (int)frame->width, 3}, detect_candidates); + + if (detect_results.size() == 1) + is_detected = true; + + if (is_detected) + { + switch (_gEvent) + { + case ENROLL: + recognizer->enroll_id((uint16_t *)frame->buf, {(int)frame->height, (int)frame->width, 3}, detect_results.front().keypoint); + ESP_LOGW("ENROLL", "ID %d is enrolled", recognizer->get_enrolled_ids().back().id); + frame_show_state = SHOW_STATE_ENROLL; + break; + + case RECOGNIZE: + recognize_result = recognizer->recognize((uint16_t *)frame->buf, {(int)frame->height, (int)frame->width, 3}, detect_results.front().keypoint); + print_detection_result(detect_results); + if (recognize_result.id > 0) + ESP_LOGI("RECOGNIZE", "Similarity: %f, Match ID: %d", recognize_result.similarity, recognize_result.id); + else + ESP_LOGE("RECOGNIZE", "Similarity: %f, Match ID: %d", recognize_result.similarity, recognize_result.id); + frame_show_state = SHOW_STATE_RECOGNIZE; + break; + + case DELETE: + recognizer->delete_id(); + ESP_LOGE("DELETE", "% d IDs left", recognizer->get_enrolled_id_num()); + frame_show_state = SHOW_STATE_DELETE; + break; + + default: + break; + } + } + + if (frame_show_state != SHOW_STATE_IDLE) + { + static int frame_count = 0; + switch (frame_show_state) + { + case SHOW_STATE_DELETE: + rgb_printf(frame, RGB565_MASK_RED, "%d IDs left", recognizer->get_enrolled_id_num()); + break; + + case SHOW_STATE_RECOGNIZE: + if (recognize_result.id > 0) + rgb_printf(frame, RGB565_MASK_GREEN, "ID %d", recognize_result.id); + else + rgb_print(frame, RGB565_MASK_RED, "who ?"); + break; + + case SHOW_STATE_ENROLL: + rgb_printf(frame, RGB565_MASK_BLUE, "Enroll: ID %d", recognizer->get_enrolled_ids().back().id); + break; + + default: + break; + } + + if (++frame_count > FRAME_DELAY_NUM) + { + frame_count = 0; + frame_show_state = SHOW_STATE_IDLE; + } + } + + if (detect_results.size()) + { + draw_detection_result((uint16_t *)frame->buf, frame->height, frame->width, detect_results); + } + } + + if (xQueueFrameO) + { + + xQueueSend(xQueueFrameO, &frame, portMAX_DELAY); + } + else if (gReturnFB) + { + esp_camera_fb_return(frame); + } + else + { + free(frame); + } + + if (xQueueResult && is_detected) + { + xQueueSend(xQueueResult, &recognize_result, portMAX_DELAY); + } + } + } +} + +static void task_event_handler(void *arg) +{ + recognizer_state_t _gEvent; + while (true) + { + xQueueReceive(xQueueEvent, &(_gEvent), portMAX_DELAY); + xSemaphoreTake(xMutex, portMAX_DELAY); + gEvent = _gEvent; + xSemaphoreGive(xMutex); + } +} + +void register_human_face_recognition(const QueueHandle_t frame_i, + const QueueHandle_t event, + const QueueHandle_t result, + const QueueHandle_t frame_o, + const bool camera_fb_return) +{ + xQueueFrameI = frame_i; + xQueueFrameO = frame_o; + xQueueEvent = event; + xQueueResult = result; + gReturnFB = camera_fb_return; + xMutex = xSemaphoreCreateMutex(); + + xTaskCreatePinnedToCore(task_process_handler, TAG, 4 * 1024, NULL, 5, NULL, 0); + if (xQueueEvent) + xTaskCreatePinnedToCore(task_event_handler, TAG, 4 * 1024, NULL, 5, NULL, 1); +} diff --git a/components/modules/ai/who_human_face_recognition.hpp b/components/modules/ai/who_human_face_recognition.hpp new file mode 100644 index 0000000..b7841d8 --- /dev/null +++ b/components/modules/ai/who_human_face_recognition.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +typedef enum +{ + IDLE = 0, + DETECT, + ENROLL, + RECOGNIZE, + DELETE, +} recognizer_state_t; + +void register_human_face_recognition(QueueHandle_t frame_i, + QueueHandle_t event, + QueueHandle_t result, + QueueHandle_t frame_o = NULL, + const bool camera_fb_return = false); From 0266875718d143668b1d99917768583bd5ef007a Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Wed, 20 Oct 2021 20:16:02 +0800 Subject: [PATCH 03/12] :sparkles: add face_recognition examples --- .../human_face_recognition/lcd/CMakeLists.txt | 8 ++ .../lcd/main/CMakeLists.txt | 5 ++ .../lcd/main/app_main.cpp | 30 +++++++ .../lcd/main/event_logic.cpp | 84 +++++++++++++++++++ .../lcd/main/event_logic.hpp | 13 +++ .../human_face_recognition/lcd/partitions.csv | 5 ++ .../lcd/sdkconfig.defaults | 12 +++ .../lcd/sdkconfig.defaults.esp32s3 | 15 ++++ .../terminal/CMakeLists.txt | 8 ++ .../terminal/main/CMakeLists.txt | 5 ++ .../terminal/main/app_main.cpp | 25 ++++++ .../terminal/main/event_logic.cpp | 84 +++++++++++++++++++ .../terminal/main/event_logic.hpp | 13 +++ .../terminal/partitions.csv | 5 ++ .../terminal/sdkconfig.defaults | 12 +++ .../terminal/sdkconfig.defaults.esp32 | 4 + .../terminal/sdkconfig.defaults.esp32s2 | 7 ++ .../terminal/sdkconfig.defaults.esp32s3 | 15 ++++ 18 files changed, 350 insertions(+) create mode 100644 examples/human_face_recognition/lcd/CMakeLists.txt create mode 100644 examples/human_face_recognition/lcd/main/CMakeLists.txt create mode 100755 examples/human_face_recognition/lcd/main/app_main.cpp create mode 100644 examples/human_face_recognition/lcd/main/event_logic.cpp create mode 100644 examples/human_face_recognition/lcd/main/event_logic.hpp create mode 100644 examples/human_face_recognition/lcd/partitions.csv create mode 100644 examples/human_face_recognition/lcd/sdkconfig.defaults create mode 100644 examples/human_face_recognition/lcd/sdkconfig.defaults.esp32s3 create mode 100644 examples/human_face_recognition/terminal/CMakeLists.txt create mode 100644 examples/human_face_recognition/terminal/main/CMakeLists.txt create mode 100755 examples/human_face_recognition/terminal/main/app_main.cpp create mode 100644 examples/human_face_recognition/terminal/main/event_logic.cpp create mode 100644 examples/human_face_recognition/terminal/main/event_logic.hpp create mode 100644 examples/human_face_recognition/terminal/partitions.csv create mode 100644 examples/human_face_recognition/terminal/sdkconfig.defaults create mode 100644 examples/human_face_recognition/terminal/sdkconfig.defaults.esp32 create mode 100644 examples/human_face_recognition/terminal/sdkconfig.defaults.esp32s2 create mode 100644 examples/human_face_recognition/terminal/sdkconfig.defaults.esp32s3 diff --git a/examples/human_face_recognition/lcd/CMakeLists.txt b/examples/human_face_recognition/lcd/CMakeLists.txt new file mode 100644 index 0000000..f3c0cfb --- /dev/null +++ b/examples/human_face_recognition/lcd/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ../../../components) +add_compile_options(-fdiagnostics-color=always) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(human_face_recognition_lcd) diff --git a/examples/human_face_recognition/lcd/main/CMakeLists.txt b/examples/human_face_recognition/lcd/main/CMakeLists.txt new file mode 100644 index 0000000..0f71747 --- /dev/null +++ b/examples/human_face_recognition/lcd/main/CMakeLists.txt @@ -0,0 +1,5 @@ +set(src_dirs .) + +set(include_dirs .) + +idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs}) \ No newline at end of file diff --git a/examples/human_face_recognition/lcd/main/app_main.cpp b/examples/human_face_recognition/lcd/main/app_main.cpp new file mode 100755 index 0000000..6bb5a3b --- /dev/null +++ b/examples/human_face_recognition/lcd/main/app_main.cpp @@ -0,0 +1,30 @@ +#include "who_camera.h" +#include "who_human_face_recognition.hpp" +#include "who_lcd.h" +#include "who_button.h" +#include "event_logic.hpp" +#include "who_adc_button.h" + +static QueueHandle_t xQueueAIFrame = NULL; +static QueueHandle_t xQueueLCDFrame = NULL; +static QueueHandle_t xQueueKeyState = NULL; +static QueueHandle_t xQueueEventLogic = NULL; +static button_adc_config_t buttons[4] = {{1, 2800, 3000}, {2, 2250, 2450}, {3, 300, 500}, {4, 850, 1050}}; + +#define GPIO_BOOT GPIO_NUM_0 + +extern "C" void app_main() +{ + xQueueAIFrame = xQueueCreate(2, sizeof(camera_fb_t *)); + xQueueLCDFrame = xQueueCreate(2, sizeof(camera_fb_t *)); + xQueueKeyState = xQueueCreate(1, sizeof(int *)); + xQueueEventLogic = xQueueCreate(1, sizeof(int *)); + + register_camera(PIXFORMAT_RGB565, FRAMESIZE_240X240, 2, xQueueAIFrame); + register_button(GPIO_BOOT, xQueueKeyState); + // register_adc_button(buttons, 4, xQueueKeyState); + register_event(xQueueKeyState, xQueueEventLogic); + register_human_face_recognition(xQueueAIFrame, xQueueEventLogic, NULL, xQueueLCDFrame, false); + register_lcd(xQueueLCDFrame, NULL, true); + +} diff --git a/examples/human_face_recognition/lcd/main/event_logic.cpp b/examples/human_face_recognition/lcd/main/event_logic.cpp new file mode 100644 index 0000000..08adeaa --- /dev/null +++ b/examples/human_face_recognition/lcd/main/event_logic.cpp @@ -0,0 +1,84 @@ +#include +#include "event_logic.hpp" +#include "who_button.h" +#include "who_human_face_recognition.hpp" + +typedef enum +{ + MENU = 1, + PLAY, + UP, + DOWN +}key_name_t; + +static QueueHandle_t xQueueKeyStateI = NULL; +static QueueHandle_t xQueueEventO = NULL; +static key_state_t key_state; +static key_name_t adc_button_name; +static recognizer_state_t recognizer_state; + +void event_generate(void *arg) +{ + while (1) + { + xQueueReceive(xQueueKeyStateI, &key_state, portMAX_DELAY); + switch (key_state) + { + case KEY_SHORT_PRESS: + recognizer_state = RECOGNIZE; + break; + + case KEY_LONG_PRESS: + recognizer_state = ENROLL; + break; + + case KEY_DOUBLE_CLICK: + recognizer_state = DELETE; + break; + + default: + recognizer_state = DETECT; + break; + } + xQueueSend(xQueueEventO, &recognizer_state, portMAX_DELAY); + } +} + + +void event_generate_from_adc_button(void *arg) +{ + while (1) + { + xQueueReceive(xQueueKeyStateI, &adc_button_name, portMAX_DELAY); + switch (adc_button_name) + { + case MENU: + recognizer_state = ENROLL; + break; + + case PLAY: + recognizer_state = DELETE; + break; + + case UP: + recognizer_state = RECOGNIZE; + break; + + case DOWN: + recognizer_state = RECOGNIZE; + break; + + default: + recognizer_state = DETECT; + break; + } + xQueueSend(xQueueEventO, &recognizer_state, portMAX_DELAY); + } +} + +void register_event(const QueueHandle_t key_state_i, const QueueHandle_t event_o) +{ + xQueueKeyStateI = key_state_i; + xQueueEventO = event_o; + xTaskCreatePinnedToCore(event_generate, "event_logic_task", 1024, NULL, 5, NULL, 0); +} \ No newline at end of file diff --git a/examples/human_face_recognition/lcd/main/event_logic.hpp b/examples/human_face_recognition/lcd/main/event_logic.hpp new file mode 100644 index 0000000..9468ba0 --- /dev/null +++ b/examples/human_face_recognition/lcd/main/event_logic.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" + +/** + * @brief + * + * @param key_state_i + * @param event_o + */ +void register_event(const QueueHandle_t key_state_i, const QueueHandle_t event_o); \ No newline at end of file diff --git a/examples/human_face_recognition/lcd/partitions.csv b/examples/human_face_recognition/lcd/partitions.csv new file mode 100644 index 0000000..d6fdf11 --- /dev/null +++ b/examples/human_face_recognition/lcd/partitions.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +factory, app, factory, 0x010000, 3840K +nvs, data, nvs, 0x3D0000, 16K +fr, 32, 32, 0x3E0000, 128K diff --git a/examples/human_face_recognition/lcd/sdkconfig.defaults b/examples/human_face_recognition/lcd/sdkconfig.defaults new file mode 100644 index 0000000..2b2b87f --- /dev/null +++ b/examples/human_face_recognition/lcd/sdkconfig.defaults @@ -0,0 +1,12 @@ +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y + +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_USE_CAPS_ALLOC=y + +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" + +CONFIG_S16=y \ No newline at end of file diff --git a/examples/human_face_recognition/lcd/sdkconfig.defaults.esp32s3 b/examples/human_face_recognition/lcd/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000..522de61 --- /dev/null +++ b/examples/human_face_recognition/lcd/sdkconfig.defaults.esp32s3 @@ -0,0 +1,15 @@ +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S3_SPIRAM_SUPPORT=y + +CONFIG_ESP32S3_DATA_CACHE_64KB=y +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y + +CONFIG_CAMERA_MODULE_ESP_S3_EYE=y +CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ST7789=y +CONFIG_ESPTOOLPY_NO_STUB=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +CONFIG_SPIRAM_MODE_OCT=y + +CONFIG_S8=y diff --git a/examples/human_face_recognition/terminal/CMakeLists.txt b/examples/human_face_recognition/terminal/CMakeLists.txt new file mode 100644 index 0000000..fa8a4b8 --- /dev/null +++ b/examples/human_face_recognition/terminal/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ../../../components) +add_compile_options(-fdiagnostics-color=always) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(human_face_recognition_terminal) diff --git a/examples/human_face_recognition/terminal/main/CMakeLists.txt b/examples/human_face_recognition/terminal/main/CMakeLists.txt new file mode 100644 index 0000000..0f71747 --- /dev/null +++ b/examples/human_face_recognition/terminal/main/CMakeLists.txt @@ -0,0 +1,5 @@ +set(src_dirs .) + +set(include_dirs .) + +idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs}) \ No newline at end of file diff --git a/examples/human_face_recognition/terminal/main/app_main.cpp b/examples/human_face_recognition/terminal/main/app_main.cpp new file mode 100755 index 0000000..a96e98a --- /dev/null +++ b/examples/human_face_recognition/terminal/main/app_main.cpp @@ -0,0 +1,25 @@ +#include "who_camera.h" +#include "who_human_face_recognition.hpp" +#include "who_button.h" +#include "event_logic.hpp" +#include "who_adc_button.h" + +static QueueHandle_t xQueueAIFrame = NULL; +static QueueHandle_t xQueueKeyState = NULL; +static QueueHandle_t xQueueEventLogic = NULL; +static button_adc_config_t buttons[4] = {{1, 2800, 3000}, {2, 2250, 2450}, {3, 300, 500}, {4, 850, 1050}}; + +#define GPIO_BOOT GPIO_NUM_0 + +extern "C" void app_main() +{ + xQueueAIFrame = xQueueCreate(2, sizeof(camera_fb_t *)); + xQueueKeyState = xQueueCreate(1, sizeof(int *)); + xQueueEventLogic = xQueueCreate(1, sizeof(int *)); + + register_camera(PIXFORMAT_RGB565, FRAMESIZE_240X240, 2, xQueueAIFrame); + register_button(GPIO_BOOT, xQueueKeyState); + register_event(xQueueKeyState, xQueueEventLogic); + register_human_face_recognition(xQueueAIFrame, xQueueEventLogic, NULL, NULL, true); + +} diff --git a/examples/human_face_recognition/terminal/main/event_logic.cpp b/examples/human_face_recognition/terminal/main/event_logic.cpp new file mode 100644 index 0000000..08adeaa --- /dev/null +++ b/examples/human_face_recognition/terminal/main/event_logic.cpp @@ -0,0 +1,84 @@ +#include +#include "event_logic.hpp" +#include "who_button.h" +#include "who_human_face_recognition.hpp" + +typedef enum +{ + MENU = 1, + PLAY, + UP, + DOWN +}key_name_t; + +static QueueHandle_t xQueueKeyStateI = NULL; +static QueueHandle_t xQueueEventO = NULL; +static key_state_t key_state; +static key_name_t adc_button_name; +static recognizer_state_t recognizer_state; + +void event_generate(void *arg) +{ + while (1) + { + xQueueReceive(xQueueKeyStateI, &key_state, portMAX_DELAY); + switch (key_state) + { + case KEY_SHORT_PRESS: + recognizer_state = RECOGNIZE; + break; + + case KEY_LONG_PRESS: + recognizer_state = ENROLL; + break; + + case KEY_DOUBLE_CLICK: + recognizer_state = DELETE; + break; + + default: + recognizer_state = DETECT; + break; + } + xQueueSend(xQueueEventO, &recognizer_state, portMAX_DELAY); + } +} + + +void event_generate_from_adc_button(void *arg) +{ + while (1) + { + xQueueReceive(xQueueKeyStateI, &adc_button_name, portMAX_DELAY); + switch (adc_button_name) + { + case MENU: + recognizer_state = ENROLL; + break; + + case PLAY: + recognizer_state = DELETE; + break; + + case UP: + recognizer_state = RECOGNIZE; + break; + + case DOWN: + recognizer_state = RECOGNIZE; + break; + + default: + recognizer_state = DETECT; + break; + } + xQueueSend(xQueueEventO, &recognizer_state, portMAX_DELAY); + } +} + +void register_event(const QueueHandle_t key_state_i, const QueueHandle_t event_o) +{ + xQueueKeyStateI = key_state_i; + xQueueEventO = event_o; + xTaskCreatePinnedToCore(event_generate, "event_logic_task", 1024, NULL, 5, NULL, 0); +} \ No newline at end of file diff --git a/examples/human_face_recognition/terminal/main/event_logic.hpp b/examples/human_face_recognition/terminal/main/event_logic.hpp new file mode 100644 index 0000000..9468ba0 --- /dev/null +++ b/examples/human_face_recognition/terminal/main/event_logic.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" + +/** + * @brief + * + * @param key_state_i + * @param event_o + */ +void register_event(const QueueHandle_t key_state_i, const QueueHandle_t event_o); \ No newline at end of file diff --git a/examples/human_face_recognition/terminal/partitions.csv b/examples/human_face_recognition/terminal/partitions.csv new file mode 100644 index 0000000..d6fdf11 --- /dev/null +++ b/examples/human_face_recognition/terminal/partitions.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +factory, app, factory, 0x010000, 3840K +nvs, data, nvs, 0x3D0000, 16K +fr, 32, 32, 0x3E0000, 128K diff --git a/examples/human_face_recognition/terminal/sdkconfig.defaults b/examples/human_face_recognition/terminal/sdkconfig.defaults new file mode 100644 index 0000000..2b2b87f --- /dev/null +++ b/examples/human_face_recognition/terminal/sdkconfig.defaults @@ -0,0 +1,12 @@ +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y + +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_USE_CAPS_ALLOC=y + +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" + +CONFIG_S16=y \ No newline at end of file diff --git a/examples/human_face_recognition/terminal/sdkconfig.defaults.esp32 b/examples/human_face_recognition/terminal/sdkconfig.defaults.esp32 new file mode 100644 index 0000000..ecc2d87 --- /dev/null +++ b/examples/human_face_recognition/terminal/sdkconfig.defaults.esp32 @@ -0,0 +1,4 @@ +CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32_SPIRAM_SUPPORT=y + +CONFIG_CAMERA_MODULE_ESP_EYE=y \ No newline at end of file diff --git a/examples/human_face_recognition/terminal/sdkconfig.defaults.esp32s2 b/examples/human_face_recognition/terminal/sdkconfig.defaults.esp32s2 new file mode 100644 index 0000000..3b5e99c --- /dev/null +++ b/examples/human_face_recognition/terminal/sdkconfig.defaults.esp32s2 @@ -0,0 +1,7 @@ +CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S2_SPIRAM_SUPPORT=y + +CONFIG_ESP32S2_DATA_CACHE_16KB=y +ESP32S2_DATA_CACHE_LINE_32B=y + +CONFIG_CAMERA_MODULE_ESP_S2_KALUGA=y \ No newline at end of file diff --git a/examples/human_face_recognition/terminal/sdkconfig.defaults.esp32s3 b/examples/human_face_recognition/terminal/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000..522de61 --- /dev/null +++ b/examples/human_face_recognition/terminal/sdkconfig.defaults.esp32s3 @@ -0,0 +1,15 @@ +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S3_SPIRAM_SUPPORT=y + +CONFIG_ESP32S3_DATA_CACHE_64KB=y +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y + +CONFIG_CAMERA_MODULE_ESP_S3_EYE=y +CONFIG_LCD_DRIVER_SCREEN_CONTROLLER_ST7789=y +CONFIG_ESPTOOLPY_NO_STUB=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +CONFIG_SPIRAM_MODE_OCT=y + +CONFIG_S8=y From c559f7fc327ba9dc6e48cffb87a590e1c97466df Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Wed, 20 Oct 2021 20:18:52 +0800 Subject: [PATCH 04/12] :art: update the input format of fb_gfx --- components/fb_gfx/CMakeLists.txt | 15 ++++++--- components/fb_gfx/fb_gfx.c | 51 +++++++++++++++++++++++------- components/fb_gfx/include/fb_gfx.h | 33 +++++++++---------- 3 files changed, 67 insertions(+), 32 deletions(-) diff --git a/components/fb_gfx/CMakeLists.txt b/components/fb_gfx/CMakeLists.txt index 5b6febc..4a456bd 100644 --- a/components/fb_gfx/CMakeLists.txt +++ b/components/fb_gfx/CMakeLists.txt @@ -1,5 +1,10 @@ -set(COMPONENT_SRCS "fb_gfx.c") -set(COMPONENT_ADD_INCLUDEDIRS "include") -set(COMPONENT_PRIV_INCLUDEDIRS "") -set(COMPONENT_PRIV_REQUIRES newlib) -register_component() \ No newline at end of file +# set(COMPONENT_SRCS "fb_gfx.c") +# set(COMPONENT_ADD_INCLUDEDIRS "include") +# set(COMPONENT_PRIV_INCLUDEDIRS "") +# set(COMPONENT_PRIV_REQUIRES newlib) +# register_component() +set(src_dirs .) +set(include_dirs . + ./include) +set(requires esp32-camera) +idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires}) \ No newline at end of file diff --git a/components/fb_gfx/fb_gfx.c b/components/fb_gfx/fb_gfx.c index 1c0a691..f539562 100644 --- a/components/fb_gfx/fb_gfx.c +++ b/components/fb_gfx/fb_gfx.c @@ -38,10 +38,24 @@ typedef struct #include "FreeMonoBold12pt7b.h" //14x24 #define gfxFont ((GFXfont *)(&FreeMonoBold12pt7b)) -void fb_gfx_fillRect(fb_data_t *fb, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) +void fb_gfx_fillRect(camera_fb_t *fb, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) { + int bytes_per_pixel = 0; + switch (fb->format) + { + case PIXFORMAT_GRAYSCALE: + bytes_per_pixel = 1; + break; + case PIXFORMAT_RGB565: + bytes_per_pixel = 2; + break; + case PIXFORMAT_RGB888: + bytes_per_pixel = 3; + default: + break; + } int32_t line_step = (fb->width - w) * 3; - uint8_t *data = fb->data + ((x + (y * fb->width)) * 3); + uint8_t *data = fb->buf + ((x + (y * fb->width)) * bytes_per_pixel); uint8_t c0 = color >> 16; uint8_t c1 = color >> 8; uint8_t c2 = color; @@ -49,26 +63,41 @@ void fb_gfx_fillRect(fb_data_t *fb, int32_t x, int32_t y, int32_t w, int32_t h, { for (int j = 0; j < w; j++) { - data[0] = c0; - data[1] = c1; - data[2] = c2; - data += 3; + switch (bytes_per_pixel) + { + case 1: + data[0] = c2; + data++; + break; + case 2: + data[0] = c1; + data[1] = c2; + data += 2; + break; + case 3: + data[0] = c0; + data[1] = c1; + data[2] = c2; + data += 3; + default: + break; + } } data += line_step; } } -void fb_gfx_drawFastHLine(fb_data_t *fb, int32_t x, int32_t y, int32_t w, uint32_t color) +void fb_gfx_drawFastHLine(camera_fb_t *fb, int32_t x, int32_t y, int32_t w, uint32_t color) { fb_gfx_fillRect(fb, x, y, w, 1, color); } -void fb_gfx_drawFastVLine(fb_data_t *fb, int32_t x, int32_t y, int32_t h, uint32_t color) +void fb_gfx_drawFastVLine(camera_fb_t *fb, int32_t x, int32_t y, int32_t h, uint32_t color) { fb_gfx_fillRect(fb, x, y, 1, h, color); } -uint8_t fb_gfx_putc(fb_data_t *fb, int32_t x, int32_t y, uint32_t color, unsigned char c) +uint8_t fb_gfx_putc(camera_fb_t *fb, int32_t x, int32_t y, uint32_t color, unsigned char c) { uint16_t line_width; uint8_t xa = 0, bit = 0, bits = 0, xx, yy; @@ -120,7 +149,7 @@ uint8_t fb_gfx_putc(fb_data_t *fb, int32_t x, int32_t y, uint32_t color, unsigne return xa; } -uint32_t fb_gfx_print(fb_data_t *fb, int x, int y, uint32_t color, const char *str) +uint32_t fb_gfx_print(camera_fb_t *fb, int x, int y, uint32_t color, const char *str) { uint32_t l = 0; int xc = x, yc = y, lc = fb->width - gfxFont->glyph[0].xAdvance; @@ -151,7 +180,7 @@ uint32_t fb_gfx_print(fb_data_t *fb, int x, int y, uint32_t color, const char *s return l; } -uint32_t fb_gfx_printf(fb_data_t *fb, int32_t x, int32_t y, uint32_t color, const char *format, ...) +uint32_t fb_gfx_printf(camera_fb_t *fb, int32_t x, int32_t y, uint32_t color, const char *format, ...) { char loc_buf[64]; char *temp = loc_buf; diff --git a/components/fb_gfx/include/fb_gfx.h b/components/fb_gfx/include/fb_gfx.h index 079ff7b..8cd4686 100644 --- a/components/fb_gfx/include/fb_gfx.h +++ b/components/fb_gfx/include/fb_gfx.h @@ -13,29 +13,30 @@ // limitations under the License. #ifndef _FB_GFX_H_ #define _FB_GFX_H_ +#include "esp_camera.h" #ifdef __cplusplus extern "C" { #endif - typedef enum { - FB_RGB888, FB_BGR888, FB_RGB565, FB_BGR565 - } fb_format_t; + // typedef enum { + // FB_RGB888, FB_BGR888, FB_RGB565, FB_BGR565 + // } fb_format_t; - typedef struct { - int width; - int height; - int bytes_per_pixel; - fb_format_t format; - uint8_t * data; - } fb_data_t; + // typedef struct { + // int width; + // int height; + // int bytes_per_pixel; + // fb_format_t format; + // uint8_t * data; + // } fb_data_t; - void fb_gfx_fillRect (fb_data_t *fb, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); - void fb_gfx_drawFastHLine(fb_data_t *fb, int32_t x, int32_t y, int32_t w, uint32_t color); - void fb_gfx_drawFastVLine(fb_data_t *fb, int32_t x, int32_t y, int32_t h, uint32_t color); - uint8_t fb_gfx_putc (fb_data_t *fb, int32_t x, int32_t y, uint32_t color, unsigned char c); - uint32_t fb_gfx_print (fb_data_t *fb, int32_t x, int32_t y, uint32_t color, const char * str); - uint32_t fb_gfx_printf (fb_data_t *fb, int32_t x, int32_t y, uint32_t color, const char *format, ...); + void fb_gfx_fillRect (camera_fb_t *fb, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color); + void fb_gfx_drawFastHLine(camera_fb_t *fb, int32_t x, int32_t y, int32_t w, uint32_t color); + void fb_gfx_drawFastVLine(camera_fb_t *fb, int32_t x, int32_t y, int32_t h, uint32_t color); + uint8_t fb_gfx_putc (camera_fb_t *fb, int32_t x, int32_t y, uint32_t color, unsigned char c); + uint32_t fb_gfx_print (camera_fb_t *fb, int32_t x, int32_t y, uint32_t color, const char * str); + uint32_t fb_gfx_printf (camera_fb_t *fb, int32_t x, int32_t y, uint32_t color, const char *format, ...); #ifdef __cplusplus } From 61402bdf2626f7cce844882570b49108dbf05d3d Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Wed, 20 Oct 2021 20:22:14 +0800 Subject: [PATCH 05/12] :heavy_plus_sign: add esp_adc --- components/modules/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/modules/CMakeLists.txt b/components/modules/CMakeLists.txt index eb75a3b..7d9770d 100644 --- a/components/modules/CMakeLists.txt +++ b/components/modules/CMakeLists.txt @@ -8,6 +8,7 @@ set(src_dirs camera lcd led + button web trace) @@ -16,6 +17,7 @@ set(include_dirs camera lcd led + button web trace) @@ -26,6 +28,7 @@ set(requires esp32-camera esp_http_server nvs_flash mdns + esp_adc_cal fb_gfx) idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires} EMBED_FILES ${embed_files}) From fb08a94c860a2ca3bdd706003ddeca30f1b9c7f4 Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Wed, 20 Oct 2021 20:22:58 +0800 Subject: [PATCH 06/12] :wrench: add model configuration --- components/modules/Kconfig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/components/modules/Kconfig b/components/modules/Kconfig index 33a17e0..1fb5363 100644 --- a/components/modules/Kconfig +++ b/components/modules/Kconfig @@ -215,4 +215,34 @@ menu "ESP-WHO Configuration" Select Camera Y9 pin. endmenu + + menu "Model Configuration" + menu "Face Recognition" + choice FACE_RECOGNITION_MODEL + bool "Face Recognition Model" + default MFN_V1 + help + Select Face Recognition Model. + + config MFN_V1 + bool "mfn v1" + + endchoice + + choice QUANTIZATION + bool "Quantization" + default S8 + help + Select Face Recognition Model. + + config S8 + bool "8-bit" + config S16 + bool "16-bit" + + endchoice + endmenu + + endmenu + endmenu \ No newline at end of file From c5fd86cf784b023ccd0996b319291ec2d47b927f Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Wed, 20 Oct 2021 20:27:49 +0800 Subject: [PATCH 07/12] :art: update xclk to 16MHz --- components/modules/camera/who_camera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/modules/camera/who_camera.h b/components/modules/camera/who_camera.h index 188bd9c..474154c 100644 --- a/components/modules/camera/who_camera.h +++ b/components/modules/camera/who_camera.h @@ -192,7 +192,7 @@ #define CAMERA_PIN_PCLK CONFIG_CAMERA_PIN_PCLK #endif -#define XCLK_FREQ_HZ 20000000 +#define XCLK_FREQ_HZ 16000000 #ifdef __cplusplus extern "C" From ea87683b1e416c17019994e2983727d0cd082879 Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Mon, 25 Oct 2021 11:51:46 +0800 Subject: [PATCH 08/12] :heavy_plus_sign: add a dependency --- components/fb_gfx/CMakeLists.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/components/fb_gfx/CMakeLists.txt b/components/fb_gfx/CMakeLists.txt index 4a456bd..4edefcb 100644 --- a/components/fb_gfx/CMakeLists.txt +++ b/components/fb_gfx/CMakeLists.txt @@ -1,8 +1,3 @@ -# set(COMPONENT_SRCS "fb_gfx.c") -# set(COMPONENT_ADD_INCLUDEDIRS "include") -# set(COMPONENT_PRIV_INCLUDEDIRS "") -# set(COMPONENT_PRIV_REQUIRES newlib) -# register_component() set(src_dirs .) set(include_dirs . ./include) From 55b06d15c299bc9a48320c6485a66062c5641212 Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Mon, 25 Oct 2021 11:54:38 +0800 Subject: [PATCH 09/12] :sparkles: update esp-dl --- components/esp-dl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esp-dl b/components/esp-dl index bad67b5..c4735f4 160000 --- a/components/esp-dl +++ b/components/esp-dl @@ -1 +1 @@ -Subproject commit bad67b57f8123e76c740ba64e0abe39aff71509d +Subproject commit c4735f45b64dd529006db116c0bf1d695e34de63 From a12e2ac39478a467174d38d1553bf171dc4fdf31 Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Mon, 25 Oct 2021 11:55:03 +0800 Subject: [PATCH 10/12] :memo: add docs --- examples/human_face_recognition/README.md | 0 examples/human_face_recognition/README_CN.md | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 examples/human_face_recognition/README.md create mode 100644 examples/human_face_recognition/README_CN.md diff --git a/examples/human_face_recognition/README.md b/examples/human_face_recognition/README.md new file mode 100644 index 0000000..e69de29 diff --git a/examples/human_face_recognition/README_CN.md b/examples/human_face_recognition/README_CN.md new file mode 100644 index 0000000..765de43 --- /dev/null +++ b/examples/human_face_recognition/README_CN.md @@ -0,0 +1,18 @@ +# Human Face Recognition Example [[English]](./README.md) + +如何运行示例可参考此[说明](../../README_CN.md),以下为使用人脸识别示例的特殊说明。 + +## 人脸识别模型配置 + +在终端输入 `idf.py menuconfig` ,依次 (Top) -> Component config -> ESP-WHO -> Model Configuration -> Face Recognition 可进入人脸识别模型配置界面,如下图所示: + +![](../../img/face_recognition_model_config.png) + +您可以在这里配置模型的版本和量化方式。 + +## 按键默认设置 + +- 交互按键为Boot键。 +- 短按按键:识别人脸。 +- 长按按键:录入人脸。 +- 双击按键:删除最后一个被录入的人脸。 From 52bf74b0a4c7aab190d8229256794b8a50ee7885 Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Mon, 25 Oct 2021 12:06:43 +0800 Subject: [PATCH 11/12] :memo: add config --- img/face_recognition_model_config.png | Bin 0 -> 49160 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 img/face_recognition_model_config.png diff --git a/img/face_recognition_model_config.png b/img/face_recognition_model_config.png new file mode 100644 index 0000000000000000000000000000000000000000..cb93b15bf54563b2d53285f5db5eb0d4e89d1dc8 GIT binary patch literal 49160 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 H$DscQf%@Ty literal 0 HcmV?d00001 From b7c8e1483fae3f270bf2e769912a42b0fbc7a7e7 Mon Sep 17 00:00:00 2001 From: yuanjiong Date: Mon, 25 Oct 2021 16:18:52 +0800 Subject: [PATCH 12/12] :memo: update doc --- examples/human_face_recognition/README.md | 18 ++++++++++++++++++ examples/human_face_recognition/README_CN.md | 10 +++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/examples/human_face_recognition/README.md b/examples/human_face_recognition/README.md index e69de29..4c9007e 100644 --- a/examples/human_face_recognition/README.md +++ b/examples/human_face_recognition/README.md @@ -0,0 +1,18 @@ +# Human Face Recognition Example [[中文]](./README_ZH.md) + +The following is the special configuration of face recognition examples. For general configuration, please refer to [this](../../README.md) + + +## Configure the face recognition model + +Enter `idf.py menuconfig` in the terminal and click (Top) -> Component config -> ESP-WHO Configuration -> Model Configuration -> Face Recognition to enter the face recognition model configuration interface, as shown below: +![](../../img/face_recognition_model_config.png) +You can configure the version and quantification type of the model here. + +## How to Use Example + +- The interactive button is the Boot button. +- Short press the button: recognize the face captured by the camera at this time. +- Long press the button: enroll the face captured by the camera at this time. +- Double click the button: delete the last enrolled face. + diff --git a/examples/human_face_recognition/README_CN.md b/examples/human_face_recognition/README_CN.md index 765de43..6f47a1c 100644 --- a/examples/human_face_recognition/README_CN.md +++ b/examples/human_face_recognition/README_CN.md @@ -1,18 +1,18 @@ # Human Face Recognition Example [[English]](./README.md) -如何运行示例可参考此[说明](../../README_CN.md),以下为使用人脸识别示例的特殊说明。 +以下为使用人脸识别示例的特殊配置,若要进行通用配置可参考此[说明](../../README_ZH.md)。 ## 人脸识别模型配置 -在终端输入 `idf.py menuconfig` ,依次 (Top) -> Component config -> ESP-WHO -> Model Configuration -> Face Recognition 可进入人脸识别模型配置界面,如下图所示: +在终端输入 `idf.py menuconfig` ,依次 (Top) -> Component config -> ESP-WHO Configuration -> Model Configuration -> Face Recognition 可进入人脸识别模型配置界面,如下图所示: ![](../../img/face_recognition_model_config.png) 您可以在这里配置模型的版本和量化方式。 -## 按键默认设置 +## 如何使用示例 - 交互按键为Boot键。 -- 短按按键:识别人脸。 -- 长按按键:录入人脸。 +- 短按按键:识别此时摄像头拍到的人脸。 +- 长按按键:录入此时摄像头拍到的人脸。 - 双击按键:删除最后一个被录入的人脸。