- 项目介绍
使用MAX78000上的板载摄像头获取画面,并利用图像处理技术对画面进行人脸识别,从而实现对画面中是否存在人脸的判断,进而判断场景中是否有人存在。
- 项目设计思路
项目一共分为四个部分:
由于这是我第一次尝试C语言开发,对环境及项目结构,配置等都比较陌生,因此这个项目主要是基于官方例程进行开发的。当检测到人脸时,板载LED会亮起。完成基本目标后,我还尝试了连接外部TFT屏幕,在屏幕上实时显示摄像头捕获的画面以及识别到的人脸。
- 关键代码展示
在解释代码功能前,我们先着重看一下对原有例程的修改和创新:
首先,原始例程中并不包括对led的控制。因此,在代码开头我们要先增加LED的支持:
#include "led.h"
MAX78000控制LED的方式非常简单,并不需要任何的初始化工作,只需要在需要点亮时使用:
LED_On(LED1);
需要关闭时使用:
LED_Off(LED1);
即可。
第二块内容是摄像头功能的启动。demo中默认是关闭摄像头功能的。摄像头在这里的开启方法比较特殊,并不在主程序的代码中定义,而是要去project.mk文件中,将PROJ_CFLAGS+=-DTFT_ENABLE去除注释。
接下来是摄像头在屏幕上的显示。特别需要注意的是,摄像头拍摄的画面,要先调整分辨率匹配屏幕尺寸,然后才可以显示,因此不可以轻易调整旋转的参数。如果显示方向和预想的不一样,可以通过翻转操作来实现。要实现翻转,需要先在main函数开头添加以下定义:
#define ROTATE_FEATHER_BOARD
接着我们在代码中找到原本#ifdef ROTATE_FEATHER_BOARD的部分,将其改为下列代码:
#ifdef ROTATE_FEATHER_BOARD
camera_set_hmirror(0);
camera_set_vflip(0);
#endif
最后,只需要把主循环while(1)中的内容改成我们需要实现的功能,即当检测到人脸时,板载LED亮起;未检测到人脸时,LED熄灭即可。
while (1) {
face_detection();
if (face_detected) {
LED_On(LED1);
face_detected = 0;
undetect_count = 0;
} else {
undetect_count++;
LED_Off(LED1);
}
下面我们说说整个项目中比较关键的功能实现方式:
我们首先根据例程中的代码初始化好摄像头。驱动摄像头的代码可以参考项目中的其他例程,
先要等待供电上电后,再开启使能引脚,完成摄像头模块的供电:
// Wait for PMIC 1.8V to become available, about 180ms after power up.
MXC_Delay(200000);
/* Enable camera power */
Camera_Power(POWER_ON);
接下来配置好DMA后,我们就可以加载摄像头驱动,完成后,可以尝试获取一下摄像头的ID信息,如果可以正确获取到,说明硬件已经识别成功。
// Initialize DMA for camera interface
MXC_DMA_Init();
dma_channel = MXC_DMA_AcquireChannel();
// Initialize the camera driver.
camera_init(CAMERA_FREQ);
// Obtain the I2C slave address of the camera.
slaveAddress = camera_get_slave_address();
PR_DEBUG("Camera I2C slave address is %02x\n", slaveAddress);
// Obtain the product ID of the camera.
ret = camera_get_product_id(&id);
if (ret != STATUS_OK) {
PR_ERR("Error returned from reading camera id. Error %d\n", ret);
return -1;
}
PR_DEBUG("Camera Product ID is %04x\n", id);
// Obtain the manufacture ID of the camera.
ret = camera_get_manufacture_id(&id);
if (ret != STATUS_OK) {
PR_ERR("Error returned from reading camera id. Error %d\n", ret);
return -1;
}
PR_DEBUG("Camera Manufacture ID is %04x\n", id);
硬件驱动正常工作后,我们就可以开始摄像头初始化的配置工作,配置好像素,大小,颜色,DMA通道等一系列参数后,记得我们还需要根据板子摆放的角度,配置好相应的翻转。因为官方的人脸识别模型对方向是敏感的,如果人脸在图像中出现了大于60度的旋转,会出现识别不到的问题,因此这里我们需要确保人脸是正的。
// Setup the camera image dimensions, pixel format and data acquiring details.
ret = camera_setup(IMAGE_XRES, IMAGE_YRES, PIXFORMAT_RGB565, FIFO_FOUR_BYTE, USE_DMA,
dma_channel);
if (ret != STATUS_OK) {
PR_ERR("Error returned from setting up camera. Error %d\n", ret);
return -1;
}
#ifdef ROTATE_FEATHER_BOARD
camera_set_hmirror(0);
camera_set_vflip(0);
#else
camera_set_vflip(0);
#endif
接下来人脸识别部分的主要代码,这些代码被单独放在了facedetection.c中:
// Enable CNN peripheral, enable CNN interrupt, turn on CNN clock
// CNN clock: 50 MHz div 1
cnn_enable(MXC_S_GCR_PCLKDIV_CNNCLKSEL_PCLK, MXC_S_GCR_PCLKDIV_CNNCLKDIV_DIV1);
/* Configure CNN 1 to detect a face */
cnn_1_init(); // Bring CNN state machine into consistent state
cnn_1_load_weights(); // Load CNN kernels
cnn_1_load_bias(); // Load CNN bias
cnn_1_configure(); // Configure CNN state machine
/* Run CNN on time on original and shifted images */
run_cnn_1(0, 0);
最后识别完成后,方框的绘制是通过这个函数实现的:
MXC_TFT_ShowImageCameraRGB565(X_START, Y_START, raw, w, h);
Led点灯在MAX78000上实现非常简单,导入相应库后,无需任何配置,直接使用即可:
#include "led.h"
LED_On(LED1);
LED_Off(LED1);
- 实现结果展示
硬件连接上我们把MAX78000和ILI9341一起插在面包板上进行连接。
接线方法如下
当未检测到人脸时,屏幕上仅显示摄像头实时画面。
当检测到人脸出现,则在人脸区域绘制黄色方框,并且亮起板载LED。
- 项目主要的不足和未来计划
一开始设想方案的初衷是通过人脸识别来完成人在检测的过程,相比使用毫米波雷达检测人在状态,这种方法具有两个明显的优势。首先,它解决了人体静止状态的检测问题。其次,通过人脸识别可以同时获取面部数据,实现对"是谁"的识别,即面部ID的匹配,为物联网后续个性化功能的实现提供便利。然而,实际开发完成后我们发现结果并不太理想。首先,为了正确识别人脸,对环境光照要求较高。其次,对于面部识别,人脸的角度和方向有较高的要求。可以从视频中观察到,如果人脸倾斜,识别效果就无法达到预期。因此,我计划在未来使用算力更强大的平台,例如Jetson Nano,来重新实现这个项目,以探索复杂模型和更强大的计算能力是否能够实现最初的设想。