esp-who的环境搭建
本小节来讲述关于猫脸检测代码的烧录和讲解。首先你需要去搭建好esp-who的开发环境,esp-who的开发环境依赖于esp-idf v4.4的发行版。
第一步:
如果你是windows用户,你可以下载一个git版本管理工具去克隆以下的代码仓库。
如果你是linux或者是macos用户,你可以直接打开终端去克隆下面的仓库
git clone https://github.com/espressif/esp-who.git
git clone https://gitee.com/EspressifSystems/esp-gitee-tools.git
克隆好上面的这两个仓库后,进入到esp-who的目录:
在终端中运行以下命令:
bash /esp-gitee-tools-path/submodule-update.sh .
这个脚本会自动的拉取esp-who的外部组件。
至此,esp-who的环境就搭建完毕了。
打开克隆好的esp-who的目录,你会看到如下图所示:
components是esp-who的外部组件,default_bin是仓库自带的默认的二进制文件,docs是文档目录 examples是示例目录 img是图片目录,tools是工具目录。
下面我就示例目录里面的cat_face_detection部分讲解一下。
猫脸检测
在猫脸检测的示例里面又有3个子目录
分别是terminal LCD web,也就是将结果分别显示在终端,LCD 网页上面。
如下图所示是猫脸检测的示例支持的开发板。
terminal
首先我们可以用idf.py命令将示例代码跑起来,你可以使用以下的命令:
idf.py set-target esp32s3
idf.py build flash monitor
这个过程中会自动编译 下载到开发板的端口上,将摄像头对猫脸,进行识别,如下图所示:
你可以打开app_main.cpp文件去查看相应的代码
#include "who_camera.h"
#include "who_cat_face_detection.hpp"
static QueueHandle_t xQueueAIFrame = NULL;
extern "C" void app_main()
{
xQueueAIFrame = xQueueCreate(2, sizeof(camera_fb_t *));
register_camera(PIXFORMAT_RGB565, FRAMESIZE_240X240, 2, xQueueAIFrame);
register_cat_face_detection(xQueueAIFrame, NULL, NULL, NULL, true);
}
下面就针对上面的这段代码进行讲解:
😀第一步:示例烧录到开发板后,摄像头开始工作,调用register_camera()的函数,将摄像头的获取的图片 一帧一帧的传输到作为帧的输出流的xQueueAIFrame队列缓冲区。
这个register_camera()函数具体是怎么用的呢?
/**
* @brief Initialize camera
*
* @param pixformat One of
* - PIXFORMAT_RGB565
* - PIXFORMAT_YUV422
* - PIXFORMAT_GRAYSC
* - PIXFORMAT_JPEG
* - PIXFORMAT_RGB888
* - PIXFORMAT_RAW
* - PIXFORMAT_RGB444
* - PIXFORMAT_RGB555
* @param frame_size One of
* - FRAMESIZE_96X96, // 96x96
* - FRAMESIZE_QQVGA, // 160x120
* - FRAMESIZE_QCIF, // 176x144
* - FRAMESIZE_HQVGA, // 240x176
* - FRAMESIZE_240X240, // 240x240
* - FRAMESIZE_QVGA, // 320x240
* - FRAMESIZE_CIF, // 400x296
* - FRAMESIZE_HVGA, // 480x320
* - FRAMESIZE_VGA, // 640x480
* - FRAMESIZE_SVGA, // 800x600
* - FRAMESIZE_XGA, // 1024x768
* - FRAMESIZE_HD, // 1280x720
* - FRAMESIZE_SXGA, // 1280x1024
* - FRAMESIZE_UXGA, // 1600x1200
* - FRAMESIZE_FHD, // 1920x1080
* - FRAMESIZE_P_HD, // 720x1280
* - FRAMESIZE_P_3MP, // 864x1536
* - FRAMESIZE_QXGA, // 2048x1536
* - FRAMESIZE_QHD, // 2560x1440
* - FRAMESIZE_WQXGA, // 2560x1600
* - FRAMESIZE_P_FHD, // 1080x1920
* - FRAMESIZE_QSXGA, // 2560x1920
* @param fb_count Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)
*/
void register_camera(const pixformat_t pixel_fromat,
const framesize_t frame_size,
const uint8_t fb_count,
const QueueHandle_t frame_o);
pixel_fromat是像素的设置,frame_size是每一帧的大小,fb_count是帧的缓冲区的数量 frame_o是帧的输出流。
xQueueAIFrame队列是调用了xQueueCreate()的函数,创建了一个队列的深度(大小)为2,队列的每一项的大小是指向camera_fb_t类型的指针类型的大小,创建完后会返回QueueHandle_t类型的队列的句柄。
xQueueAIFrame = xQueueCreate(2, sizeof(camera_fb_t *));
😀第二步:此时xQueueAIFrame队列缓冲区里面放置着摄像头获取到的图片,调用register_cat_face_detection()的函数,将xQueueAIFrame队列缓冲区作为帧的输入流传入,register_cat_face_detection()函数中会通过AI模型计算,识别到猫脸,然后帧的输出流设置为NULL,至此猫脸检测的结果就显示到终端上了。
整个流程如下图所示:
接下来我们看register_cat_face_detection()函数里面具体是怎么实现操作的:
在猫脸检测的函数中,调用xTaskCreatePinnedToCore将task_process_handler函数创建到freeRTOS上
在task_process_handler函数中做了一件什么事情呢?
static void task_process_handler(void *arg)
{
camera_fb_t *frame = NULL;
CatFaceDetectMN03 detector(0.4F, 0.3F, 10, 0.3F);
while (true)
{
if (gEvent)
{
bool is_detected = false;
//从xQueueFrameI中接收一项数据
if (xQueueReceive(xQueueFrameI, &frame, portMAX_DELAY))
{
//AI模型计算
std::list<dl::detect::result_t> &detect_results = detector.infer((uint16_t *)frame->buf, {(int)frame->height, (int)frame->width, 3});
if (detect_results.size() > 0)
{
draw_detection_result((uint16_t *)frame->buf, frame->height, frame->width, detect_results);
print_detection_result(detect_results);
is_detected = true;
}
}
if (xQueueFrameO)
{
//向xQueueFrameO发送一项数据
xQueueSend(xQueueFrameO, &frame, portMAX_DELAY);
}
else if (gReturnFB)
{
// /**
// * @brief Return the frame buffer to be reused again.返回要再次重用的帧缓冲区。
// *
// * @param fb Pointer to the frame buffer
// */
// void esp_camera_fb_return(camera_fb_t * fb);
esp_camera_fb_return(frame);
}
else
{
free(frame);
}
if (xQueueResult)
{
//向xQueueResult发送一项数据
xQueueSend(xQueueResult, &is_detected, portMAX_DELAY);
}
}
}
}
首先如果xQueueFrameI队列缓冲区中有数据,可以调用xQueueReceive()的函数去接收一项;如果xQueueFrameI队列缓冲区中没有数据,则不去接收。当拿到了这项数据后,经过AI模型的计算,得出每一帧的结果。这时,再去调用xQueueSend()函数向xQueueFrameO队列缓冲区中发送一项数据。
如果事件队列不为空,则调用xTaskCreatePinnedToCore()函数将task_event_handler创建到freeRTOS上运行。
task_event_handler()中不断的从xQueueEvent中接收一项。
lcd
如果你已经理解了猫脸检测输出到终端的示例代码,那么LCD的也就变得简单了。
#include "who_camera.h"
#include "who_cat_face_detection.hpp"
#include "who_lcd.h"
#include "who_trace.h"
static QueueHandle_t xQueueAIFrame = NULL;
static QueueHandle_t xQueueLCDFrame = NULL;
extern "C" void app_main()
{
xQueueAIFrame = xQueueCreate(2, sizeof(camera_fb_t *));
xQueueLCDFrame = xQueueCreate(2, sizeof(camera_fb_t *));
register_camera(PIXFORMAT_RGB565, FRAMESIZE_240X240, 2, xQueueAIFrame);
register_cat_face_detection(xQueueAIFrame, NULL, NULL, xQueueLCDFrame, false);
register_lcd(xQueueLCDFrame, NULL, true);
}
运行示例代码,将摄像头对准猫脸,监视器输出如下:
😁示例烧录到开发板后,摄像头开始工作,调用register_camera()的函数,将摄像头的获取的图片 一帧一帧的传输到作为帧的输出流的xQueueAIFrame队列缓冲区。xQueueAIFrame队列缓冲区里面放置着摄像头获取到的图片,调用register_cat_face_detection()的函数,将xQueueAIFrame队列缓冲区作为帧的输入流传入,register_cat_face_detection()函数中会通过AI模型计算,识别到猫脸。如何将经过模型计算的每一帧的数据输出到LCD屏幕上呢?
我们需要一个作为帧输出流的队列缓冲区去接收经过模型计算的每一帧的数据,在本示例中就是xQueueLCDFrame,再调用register_lcd()函数将xQueueLCDFrame()队列作为帧的输入流传入,到此就显示到LCD屏幕上了。
register_lcd()中也是先去初始化LCD的驱动,进行一些LCD的配置,紧接着调用xTaskCreatePinnedToCore()函数去调用一个任务函数。
web
理解了LCD的部分,那么web的代码就是将输出到LCD的队列xQueueLCDFrame改成了xQueueHttpFrame队列,最后调用register_httpd()方法将xQueueHttpFrame队列做为帧的输入流传入即可。
#include "who_camera.h"
#include "who_cat_face_detection.hpp"
#include "app_wifi.h"
#include "app_httpd.hpp"
#include "app_mdns.h"
static QueueHandle_t xQueueAIFrame = NULL;
static QueueHandle_t xQueueHttpFrame = NULL;
extern "C" void app_main()
{
app_wifi_main();
app_mdns_main();
xQueueAIFrame = xQueueCreate(2, sizeof(camera_fb_t *));
xQueueHttpFrame = xQueueCreate(2, sizeof(camera_fb_t *));
register_camera(PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2, xQueueAIFrame);
register_cat_face_detection(xQueueAIFrame, NULL, NULL, xQueueHttpFrame);
register_httpd(xQueueHttpFrame, NULL, true);
}
register_httpd()当你在打开网页后,触碰网页上的一些按钮,就会自动的触发一些方法。