搜索
bottom↓
回复: 0
打印 上一主题 下一主题

《DNESP32S3使用指南-IDF版_V1.6》第五十九章 人脸识别实验

[复制链接]

出0入234汤圆

跳转到指定楼层
1
发表于 2024-8-8 18:02:13 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

1)实验平台:正点原子ESP32S3开发板
2)购买链接:https://detail.tmall.com/item.htm?id=768499342659
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-347618-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子手把手教你学ESP32S3快速入门视频教程:https://www.bilibili.com/video/BV1sH4y1W7Tc
6)正点原子FPGA交流群:132780729



第五十九章 人脸识别实验

       人脸识别是一种基于人的脸部特征信息进行身份识别的一种生物识别技术。它使用摄像机或摄像头采集含有人脸的图像或视频流,并自动在图像中检测和跟踪人脸,进而对检测到的人脸进行脸部识别的一系列相关技术。本章,我们使用乐鑫AI库来实现人脸识别功能。
       本章分为如下几个部分:
       59.1 硬件设计
       59.2 软件设计
       59.3 下载验证

       59.1 硬件设计

       1. 例程功能
       本章实验功能简介:使用乐鑫官方的ESP32-WHO AI库对OV2640和OV5640摄像头输出的数据进行人脸识别。当长按BOOT按键时,录入当前对焦的人脸;当单击BOOT按键时,识别当前人脸,识别时需和人脸仓库中的人脸匹配;当双击BOOT按键时,删除当前人脸,但前提是这个张人脸之前已经在人脸仓库当中。

       2. 硬件资源

       1)LED灯
              LED-IO1

       2)XL9555
              IIC_INT-IO0(需在P5连接IO0)
              IIC_SDA-IO41
              IIC_SCL-IO42

       3)SPILCD
              CS-IO21
              SCK-IO12
              SDA-IO11
              DC-IO40(在P5端口,使用跳线帽将IO_SET和LCD_DC相连)
              PWR- IO1_3(XL9555)
              RST- IO1_2(XL9555)

       4)CAMERA
              OV_SCL-IO38
              OV_SDA- IO39
              VSYNC- IO47
              HREF- IO48
              PCLK- IO45
              D0- IO4
              D1- IO5
              D2- IO6
              D3- IO7
              D4- IO15
              D5- IO16
              D6- IO17
              D7- IO18
              RESET-IO0_5(XL9555)
              PWDN-IO0_4(XL9555)

       3. 原理图
       本章实验使用的KPU为ESP32-S3的内部资源,因此并没有相应的连接原理图。

       59.2 软件设计

       59.2.1 程序流程图
       程序流程图能帮助我们更好的理解一个工程的功能和实现的过程,对学习和设计工程有很好的主导作用。下面看看本实验的程序流程图:

图59.2.1.1 程序流程图

       59.2.2 程序解析
       在本章节中,我们将重点关注两个文件:esp_face_recognition.cpp和esp_face_ recognition.hpp。其中,esp_face_recognition.hpp主要声明了esp_face_recognition函数,其内容相对简单,因此我们暂时不作详细解释。本章节的核心关注点是esp_face_recognition.cpp文件中的函数。
       接下来,我们将详细解析esp_face_ recognition_ai_strat函数的工作原理。
  1. /**
  2. * @brief       AI图像数据开启
  3. * @param       无
  4. * @retval      1:创建失败;0:创建成功
  5. */
  6. uint8_t esp_face_recognition_ai_strat(void)
  7. {
  8.     /* 创建队列及任务 */
  9.     xQueueFrameO = xQueueCreate(5, sizeof(camera_fb_t *));
  10.     xQueueAIFrameO = xQueueCreate(5, sizeof(camera_fb_t *));
  11.     xQueueKeyState = xQueueCreate(1, sizeof(int *));
  12.     xQueueEventLogic = xQueueCreate(1, sizeof(int *));
  13.     xMutex = xSemaphoreCreateMutex();
  14.     /* 初始化按键 */
  15.     esp_key_init(GPIO_NUM_0);
  16.     /* 创建任务 */
  17.     xTaskCreatePinnedToCore(esp_key_trigger,"esp_key_scan",1024,NULL,5, NULL,0);
  18. xTaskCreatePinnedToCore(esp_event_generate, "event_logic",1024,NULL,
  19. 5, NULL,0);
  20. xTaskCreatePinnedToCore(esp_camera_process_handler, "esp_camera_process ",
  21. 4 * 1024, NULL, 5, &camera_task_handle, 1);
  22. xTaskCreatePinnedToCore(esp_ai_process_handler, "esp_ai_process_handler",
  23. 6 * 1024, NULL, 5, &ai_task_handle, 1);
  24. xTaskCreatePinnedToCore(esp_task_event_handler, "esp_task_event_handler",
  25. 4 * 1024, NULL, 5, NULL, 1);
  26.    
  27.     if (xQueueFrameO != NULL
  28.         || xQueueAIFrameO != NULL
  29.         || xQueueEventLogic != NULL
  30.         || camera_task_handle != NULL
  31.         || ai_task_handle != NULL)
  32.     {
  33.         return 0;
  34.     }

  35. return 1;
  36. }
复制代码
       在上述函数中,我们创建了四个消息队列以递交消息,并设置了一个互斥锁来防止任务优先级翻转。此外,还定义了五个任务:检测KEY按键状态(用于判断按键处于哪种状态,如长按、点击和双击等)、事件任务、摄像头获取任务、AI处理任务和按键扫描任务任务。
       下面作者分为介绍这些任务的实现流程,如下:

       1,检测KEY按键状态任务
  1. /**
  2. * @brief       按键扫描(判断短按、长按及双击状态)
  3. * @param       ticks_to_wait:等待时间
  4. * @retval      返回按键状态
  5. */
  6. int esp_key_scan(TickType_t ticks_to_wait)
  7. {
  8.     gpio_num_t io_num;
  9.     BaseType_t press_key = pdFALSE;
  10.     BaseType_t lift_key = pdFALSE;
  11.     int64_t backup_time = 0;
  12.     int64_t interval_time = 0;
  13.     static int64_t last_time = 0;

  14.     while (1)
  15.     {
  16.         xQueueReceive(gpio_evt_queue, &io_num, ticks_to_wait);

  17.         if (gpio_get_level(io_num) == 0)
  18.         {
  19.             press_key = pdTRUE;
  20.             backup_time = esp_timer_get_time();
  21.             interval_time = backup_time - last_time;
  22.         }
  23.         else if (press_key)
  24.         {
  25.             lift_key = pdTRUE;
  26.             last_time = esp_timer_get_time();
  27.             backup_time = last_time - backup_time;
  28.         }

  29.         if (press_key & lift_key)
  30.         {
  31.             press_key = pdFALSE;
  32.             lift_key = pdFALSE;

  33.             if (backup_time > LONG_PRESS_THRESH)
  34.             {
  35.                 return KEY_LONG_PRESS;
  36.             }
  37.             else
  38.             {
  39.                 if ((interval_time < DOUBLE_CLICK_THRESH)&&(interval_time > 0))
  40.                     return KEY_DOUBLE_CLICK;
  41.                 else
  42.                     return KEY_SHORT_PRESS;
  43.             }
  44.         }
  45.     }
  46. }

  47. /**
  48. * @brief       按键任务
  49. * @param       arg:未使用
  50. * @retval      无
  51. */
  52. static void esp_key_trigger(void *arg)
  53. {
  54.     arg = arg;
  55.     int ret = 0;

  56.     while (1)
  57.     {
  58.         ret = esp_key_scan(portMAX_DELAY);
  59.         xQueueOverwrite(xQueueKeyState, &ret);
  60.     }

  61.     vTaskDelete(NULL);
  62. }
复制代码
       在上述任务函数中,首先通过调用esp_key_scan函数获取按键的状态,包括长按、双击或点击等。然后,使用FreeRTOS API函数xQueueOverwrite将消息发送到事件任务中。

       2,事件任务

  1. /**
  2. * @brief       事件生成任务
  3. * @param       arg:未使用
  4. * @retval      无
  5. */
  6. void esp_event_generate(void *arg)
  7. {
  8.     arg = arg;
  9.     static key_state_t key_state;

  10.     while (1)
  11.     {
  12.         /* 接收状态 */
  13.         xQueueReceive(xQueueKeyState, &key_state, portMAX_DELAY);
  14.         /* 判断状态 */
  15.         switch (key_state)
  16.         {
  17.             case KEY_SHORT_PRESS:   /* 短按状态 */
  18.                 recognizer_state = RECOGNIZE;
  19.                 break;

  20.             case KEY_LONG_PRESS:    /* 长按状态 */
  21.                 recognizer_state = ENROLL;
  22.                 break;

  23.             case KEY_DOUBLE_CLICK:  /* 双击状态 */
  24.                 recognizer_state = DELETE;
  25.                 break;

  26.             default:
  27.                 recognizer_state = DETECT;
  28.                 break;
  29.         }
  30.         /* 发送状态 */
  31.         xQueueSend(xQueueEventLogic, &recognizer_state, portMAX_DELAY);
  32. }
  33. }
复制代码
       上述任务函数调用xQueueReceive函数接收KEY的消息,然后根据消息判断处于哪个事件状态,最后调用xQueueSend函数发送事件消息至按键扫描任务处理。

       3,按键扫描任务
  1. /**
  2. * @brief       按键扫描任务
  3. * @param       arg:未使用
  4. * @retval      无
  5. */
  6. static void esp_task_event_handler(void *arg)
  7. {
  8.     arg = arg;
  9.     recognizer_state_t _gEvent;

  10.     while (1)
  11.     {
  12.         xQueueReceive(xQueueEventLogic, &(_gEvent), portMAX_DELAY);
  13.         xSemaphoreTake(xMutex, portMAX_DELAY);
  14.         gEvent = _gEvent;
  15.         xSemaphoreGive(xMutex);
  16.     }
  17. }
复制代码
       该任务函数就是为了防止优先级翻转问题。

       4,摄像头任务

  1. /**
  2. * @brief       摄像头图像数据获取任务
  3. * @param       arg:未使用
  4. * @retval      无
  5. */
  6. static void esp_camera_process_handler(void *arg)
  7. {
  8.     arg = arg;
  9.     camera_fb_t *camera_frame = NULL;

  10.     while (1)
  11.     {
  12.         /* 获取摄像头图像 */
  13.         camera_frame = esp_camera_fb_get();

  14.         if (camera_frame)
  15.         {
  16.             /* 以队列的形式发送 */
  17.             xQueueSend(xQueueFrameO, &camera_frame, portMAX_DELAY);
  18.         }
  19.     }
  20. }
复制代码
       该任务函数最主要的作用是获取摄像头的图像数据,并发送图像数据至AI处理任务。

       5,AI处理任务函数
  1. /**
  2. * @brief       摄像头图像数据传入AI处理任务
  3. * @param       arg:未使用
  4. * @retval      无
  5. */
  6. static void esp_ai_process_handler(void *arg)
  7. {
  8.     arg = arg;
  9.     camera_fb_t *frame = NULL;
  10.     HumanFaceDetectMSR01 detector(0.3F, 0.3F, 10, 0.3F);
  11.     HumanFaceDetectMNP01 detector2(0.4F, 0.3F, 10);
  12.     FaceRecognition112V1S16 *recognizer = new FaceRecognition112V1S16();

  13.     show_state_t frame_show_state = SHOW_STATE_IDLE;
  14.     recognizer_state_t _gEvent;
  15. recognizer->set_partition(ESP_PARTITION_TYPE_DATA,
  16. ESP_PARTITION_SUBTYPE_ANY, "fr");
  17.     recognizer->set_ids_from_flash();

  18.     while(1)
  19.     {
  20.         xSemaphoreTake(xMutex, portMAX_DELAY);
  21.         _gEvent = gEvent;
  22.         gEvent = DETECT;
  23.         xSemaphoreGive(xMutex);

  24.         if (_gEvent)
  25.         {
  26.             bool is_detected = false;

  27.             if (xQueueReceive(xQueueFrameO, &frame, portMAX_DELAY))
  28.             {
  29.                 std::list<dl::detect::result_t> &detect_candidates =
  30. detector.infer((uint16_t *)frame->buf, {(int)frame->height,
  31. (int)frame->width, 3});
  32.                 std::list<dl::detect::result_t> &detect_results =
  33. detector2.infer((uint16_t *)frame->buf,
  34. {(int)frame->height,
  35. (int)frame->width, 3},
  36. detect_candidates);

  37.                 if (detect_results.size() == 1)
  38.                     is_detected = true;

  39.                 if (is_detected)
  40.                 {
  41.                     switch (_gEvent)
  42.                     {
  43.                         /* 注册 */
  44.                         case ENROLL:
  45.                             recognizer->enroll_id((uint16_t *)frame->buf,
  46. {(int)frame->height,
  47. (int)frame->width, 3},
  48. detect_results.front().keypoint,
  49. "", true);
  50.                             ESP_LOGW("ENROLL", "ID %d is enrolled",
  51. recognizer->get_enrolled_ids().back().id);
  52.                             frame_show_state = SHOW_STATE_ENROLL;
  53.                             break;
  54.                         /* 识别 */
  55.                         case RECOGNIZE:
  56.                             recognize_result = recognizer->recognize(
  57. (uint16_t *)frame->buf,
  58. {(int)frame->height,
  59. (int)frame->width, 3},
  60. detect_results.front().keypoint);
  61.                             print_detection_result(detect_results);
  62.                             if (recognize_result.id > 0)
  63.                                 ESP_LOGI("RECOGNIZE", "Similarity: %f, Match ID: %d",
  64. recognize_result.similarity, recognize_result.id);
  65.                             else
  66.                                 ESP_LOGE("RECOGNIZE", "Similarity: %f, Match ID: %d",
  67. recognize_result.similarity, recognize_result.id);
  68.                             frame_show_state = SHOW_STATE_RECOGNIZE;
  69.                             break;
  70.                         /* 删除 */
  71.                         case DELETE:
  72.                             vTaskDelay(10);
  73.                             recognizer->delete_id(true);
  74.                             ESP_LOGE("DELETE", "% d IDs left",
  75. recognizer->get_enrolled_id_num());
  76.                             frame_show_state = SHOW_STATE_DELETE;
  77.                             break;

  78.                         default:
  79.                             break;
  80.                     }
  81.                 }

  82.                 if (frame_show_state != SHOW_STATE_IDLE)
  83.                 {
  84.                     static int frame_count = 0;

  85.                     switch (frame_show_state)
  86.                     {
  87.                         /* 删除图像 */
  88.                         case SHOW_STATE_DELETE:
  89.                             esp_rgb_printf(frame, RGB565_MASK_RED,
  90. "%d IDs left",
  91. recognizer->get_enrolled_id_num());
  92.                             break;
  93.                         /* 图像识别 */
  94.                         case SHOW_STATE_RECOGNIZE:
  95.                             if (recognize_result.id > 0)
  96.                                 esp_rgb_printf(frame, RGB565_MASK_GREEN,
  97.                                               "ID %d",
  98. recognize_result.id);
  99.                             else
  100.                                 esp_rgb_print(frame, RGB565_MASK_RED, "who ?");
  101.                             break;
  102.                         /* 图像注册 */
  103.                         case SHOW_STATE_ENROLL:
  104.                             esp_rgb_printf(frame, RGB565_MASK_BLUE,
  105. "Enroll: ID %d",
  106. recognizer->get_enrolled_ids().back().id);
  107.                             break;

  108.                         default:
  109.                             break;
  110.                     }

  111.                     if (++frame_count > FRAME_DELAY_NUM)
  112.                     {
  113.                         frame_count = 0;
  114.                         frame_show_state = SHOW_STATE_IDLE;
  115.                     }
  116.                 }

  117.                 if (detect_results.size())
  118.                 {
  119.                     draw_detection_result((uint16_t *)frame->buf,
  120. frame->height, frame->width, detect_results);
  121.                 }
  122.             }

  123.             if (xQueueAIFrameO)
  124.             {

  125.                 xQueueSend(xQueueAIFrameO, &frame, portMAX_DELAY);
  126.             }
  127.             else if (gReturnFB)
  128.             {
  129.                 esp_camera_fb_return(frame);
  130.             }
  131.             else
  132.             {
  133.                 free(frame);
  134.             }
  135.         }
  136.     }
  137. }
复制代码
       上述任务函数主要负责处理图像数据,并将其提交给AI库进行处理。根据按键的不同状态,系统会执行不同的操作:

       1,如果长按按键,系统会将人脸数据传入分区表。

       2,如果点击按键,系统将当前识别的人脸与人脸库中的人脸进行对比。如果匹配成功,系统将提示人脸识别成功的信息。

       3,如果双击按键,系统同样会将当前识别的人脸与人脸库中的人脸进行对比。如果匹配成功,系统不仅会提示人脸识别成功的信息,还会从人脸库中删除匹配的人脸图像数据。

       59.3 下载验证
       如果在检测过程中发现人脸,需要长按BOOT按键,将人脸信息录入人脸存储区。当再次检测到人脸时,只需短按BOOT按键,系统就会识别当前的人脸(与存储区的人脸数据进行匹配)。如果识别成功,该系统会将人脸识别处理的图像数据显示在LCD上,否则,提示人脸识别失败,如下图所示。

图59.3.1 人脸识别效果图

阿莫论坛20周年了!感谢大家的支持与爱护!!

阿莫论坛才是最爱国的,关心国家的经济、社会的发展、担心国家被别国牵连卷入战争、知道珍惜来之不易的和平发展,知道师夷之长,关注世界的先进文化与技术,也探讨中国文化的博大精深,也懂得警惕民粹主义的祸国殃民等等等等,无不是爱国忧民的表现。(坛友:tianxian)
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-9-17 18:23

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表