/ ai资讯

基于RV1126开发板实现人脸识别方案

发布时间:2025-04-21 11:47:24

1. 方案简介

人脸识别:在图像中找出人脸,并与数据库进行比对,得出该人脸对应的身份信息。

方案设计逻辑流程图,方案代码分为分为三个业务流程,主体代码负责抓取、合成图像,算法代码负责人脸识别功能,按键监听负责修改数据库工作状态。

2. 快速上手

2.1 开发环境准备

如果您初次阅读此文档,请阅读《入门指南/开发环境准备/Easy-Eai编译环境准备与更新》,并按照其相关的操作,进行编译环境的部署

在PC端Ubuntu系统中执行run脚本,进入EASY-EAI编译环境,具体如下所示。

cd ~/develop_environment ./run.sh

2.2 源码下载以及实例编译

在EASY-EAI编译环境下创建存放源码仓库的管理目录:

cd /opt mkdir EASY-EAI-Toolkit cd EASY-EAI-Toolkit

通过git工具,在管理目录内克隆远程仓库

git clone https://github.com/EASY-EAI/EASY-EAI-Toolkit-C-Solution.git

注:

* 此处可能会因网络原因造成卡顿,请耐心等待。

* 如果实在要在gitHub网页上下载,也要把整个仓库下载下来,不能单独下载本实例对应的目录。

进入到对应的例程目录执行编译操作,具体命令如下所示:

cd EASY-EAI-Toolkit-C-Solution/solu-faceRecognition/ ./build.sh

注:

* 由于依赖库部署在板卡上,因此交叉编译过程中必须保持adb连接。

注:

* 若build.sh脚本不带任何参数,则仅会拷贝solution编译出来的可执行文件。

* 若build.sh脚本带有cpres参数,则会把Release/目录下的所有资源都拷贝到开发板上。

* 若build.sh脚本带有clear参数,则会把build/目录和Release/目录删除。

2.3 模型获取

本方案用到两个模型:face_detect.model和face_recognition.model

直接把模型下载到本地Windows主机,复制

进入PC端Ubuntu创建存放model目录:

cd /opt mkdir model

然后把模型从本地Windows主机粘贴到PC端Ubuntu中:

2.4 方案部署

使用下方命令再次回到开发实例目录

cd /opt/EASY-EAI-Toolkit-C-Solution/solu-faceRecognition/

然后,将EASY-EAI编译环境的编译结果部署到板卡中(有两种方法)。

方法一:通过执行以下命令手动部署【推荐】

cp Release/solu-* /mnt/userdata/Solu

方法二:在编译时加上编译参数自动部署

./build.sh cpres

最后,将准备好的模型部署到板卡中(注意:模型要放到编译结果的同一目录中),执行命令如下所示。

cp /opt/model/face_detect.model /mnt/userdata/Solu cp /opt/model/face_recognition.model /mnt/userdata/Solu

2.5 示例方案运行

通过按键Ctrl Shift T创建一个新窗口,执行adb shell命令,进入板卡运行环境。

adb shell

进入板卡后,定位到例程部署的位置,如下所示:

cd /userdata/Solu

运行例程命令如下所示:

./solu-faceRecognition

2.6 运行效果

运行打印:

[root@EASY-EAI-NANO:/userdata/Solu]# ./solu-faceRecognition media get entity by name: rkcif-lvds-subdev is null media get entity by name: rkcif-lite-lvds-subdev is null media get entity by name: rkisp-mpfbc-subdev is null media get entity by name: rkisp_dmapath is null media get entity by name: rkisp-mpfbc-subdev is null media get entity by name: rkisp_dmapath is null media get entity by name: rockchip-mipi-dphy-rx is null [10:20:45.318138][CAMHW]:XCAM ERROR CamHwIsp20.cpp:928: No free isp&ispp needed by fake camera! Rga built version:1.04 7b33191 2022-05-12 19:00:07 Had init the rga dev ctx = 0x62288 Rga built version:1.04 7b33191 2022-05-12 19:00:07 get rkispp_input_params devname: /dev/video35 subscribe events from /dev/video35 success ! get rkispp_input_params devname: /dev/video43 subscribe events from /dev/video43 success ! mipicamera_init: RGB aiq status ok. [INFO]rkisp_get_media_topology2:1244: Get media device: /dev/media0 info [INFO]rkisp_get_media_topology2:1244: Get media device: /dev/media1 info >>>>>sensor entity name: m01_f_gc2093 1-007e get rkisp-isp-subdev devname: /dev/v4l-subdev5 get rkisp-input-params devname: /dev/video15 get rkisp-statistics devname: /dev/video14 [INFO]rkisp_get_media_topology2:1244: Get media device: /dev/media2 info [INFO]rkisp_get_media_topology2:1244: Get media device: /dev/media3 info get rkispp_m_bypass devname: /dev/video30 get rkispp_scale0 devname: /dev/video31 get rkispp_scale1 devname: /dev/video32 get rkispp_scale2 devname: /dev/video33 [INFO]rkisp_get_media_topology2:1244: Get media device: /dev/media4 info [INFO]rkisp_get_media_topology2:1244: Get media device: /dev/media5 info rkisp_open_device2: /dev/video31 [INFO]rkisp_get_fmt:548: Get Driver default fmt: fcc NV12 [1280x720] face detect init! ##RKMEDIA Log level: 2 [RKMEDIA][SYS][Info]:text is all=2 [RKMEDIA][SYS][Info]:module is all, log_level is 2 [RKMEDIA][SYS][Info]:RK_MPI_VO_CreateChn: Enable VO[1] Start... [RKMEDIA][SYS][Info]:conn id : 56, enc id: 55, crtc id: 53, plane id: 52, w/h: 720,1280, fps: 58 [RKMEDIA][SYS][Info]:RK_MPI_VO_CreateChn: Enable VO[1] End! [RKMEDIA][SYS][Info]:RK_MPI_VO_CreateChn: Enable VO[0] Start... [RKMEDIA][SYS][Info]:conn id : 56, enc id: 55, crtc id: 53, plane id: 54, w/h: 720,1280, fps: 58 [RKMEDIA][SYS][Info]:RK_MPI_VO_CreateChn: Enable VO[0] End! #CHN[0]:IN<0,0,720,1280> --> Out<0,0,720,1280> [RKMEDIA][SYS][Info]:RK_MPI_RGA_CreateChn: Enable RGA[0], Rect<0,0,720,1280> Start... [RKMEDIA][SYS][Info]:FilterFlow:rkrga: Enable BufferPool! memtype:hw_mem, memcnt:2 [RKMEDIA][SYS][Info]:Opened DRM device /dev/dri/card0: driver rockchip version 2.0.0. [RKMEDIA][SYS][Info]:RK_MPI_RGA_CreateChn: Enable RGA[0], Rect<0,0,720,1280> End... #Bind rga[0] to VM[0]:Chn[0].... [RKMEDIA][SYS][Info]:RK_MPI_SYS_Bind: Bind Mode[RGA]:Chn[0] to Mode[VMIX]:Chn[0]... #Bind VMX[0] to VO[0].... [RKMEDIA][SYS][Info]:RK_MPI_SYS_Bind: Bind Mode[VMIX]:Chn[0] to Mode[VO]:Chn[0]... [RKMEDIA][SYS][Warn]:RK_MPI_SYS_Bind: SrcChn:VMIX[0]:Chn[x] status(3) invalid! librknn_runtime version 1.7.1 (97198ce build: 2021-11-24 09:32:17 base: 1131) face recognition init! librknn_runtime version 1.7.1 (97198ce build: 2021-11-24 09:32:17 base: 1131) database_init OK

用人脸对准摄像头,如果检测到人脸。后台会打印出识别出人脸的耗时,以及与数据库特征值比较的相似度,详情如下图所示。

(若画面中有多张人脸,则以最大一张人脸作为识别依据)

此时尚未录入人脸数据,程序只是检测到人脸,并不能识别出具体用户,则对人脸位置标记出红框。效果如下图所示。

注册人脸:用摄像头对准人脸时,按下复用按键“RECOVER”,则可成功对人脸进行注册。按键位置如下图所示。

再次识别,此时识别出用户后,则用绿框标记。如下图所示。

在识别出用户的同时,可以观察后台打印。此时会打印出与画面中最为相似的用户id与姓名,以及具体的相似度。如下图所示。

2.7 开机启动

首先进入板卡环境,执行以下命令,在板卡上创建一个给本例程使用的应用目录:myapp

cd /userdata/apps/ mkdir myapp

然后回到开发环境中,通过使用“2.4方案部署”类似的操作方法,把本例程所需要的全部文件,包含:编译结果,配置文件,模型等。部署到刚刚新建的myapp目录中。

最后在板卡上创建一个run.sh脚本来管控用户所有需要的应用即可,《入门指南/应用程序开机自启动》会详细描述run.sh脚本该如何编写。

3. 代码解析

方案主逻辑代码位于:EASY-EAI-Toolkit-C-Solution/solu-faceRecognition/src/main.cpp。代码实现主要通过调用我司的easyeai-api库快速实现人脸识别功能,代码主体分为主线程、算法分析子线程和按键监听子线程。

3.1 组件库组成

要实现人脸识别功能,需要使用到easyeai-api库的以下组件,如下所示。

模组信息如下所示。

组件 头文件以及库路径 描述
系统操作组件 easyeai-api/common_api/system_opt 提供线程操作函数
摄像头组件 easyeai-api/peripheral_api/camera 提供摄像头操作函数
显示屏组件 easyeai-api/peripheral_api/display 提供显示屏操作函数
平面几何组件 easyeai-api/peripheral_api/geometry 提供简单几何运算函数
人脸检测组件 easyeai-api/algorithm_api/face_detect 提供人脸检测操作函数
人脸校正组件 easyeai-api/algorithm_api/face_alignment 提供人脸校正操作函数
人脸识别组件 easyeai-api/algorithm_api/face_recognition 提供人脸识别操作函数

这些组件通过CMakeLists.txt编译进工程,具体请看后续章节。

3.2 逻辑框图

项目的整体逻辑框图如下所示。

3.3 主线程

主线程处理的业务有:

  • 初始化外设;
  • 创建算法分析子线程;
  • 抓图发送给到子线程;
  • 抓图、显示;

本处附上主要的逻辑功能代码,其他辅助的、校验型的代码先忽略。

组件初始化操作如下,本处调用RGB摄像头和IR摄像头。

// 1.打开摄像头 ret = rgbcamera_init(CAMERA_WIDTH, CAMERA_HEIGHT, 90); pRGBbuf= NULL; pRGBbuf= (char *)malloc(IMAGE_SIZE); ret = ircamera_init(CAMERA_WIDTH, CAMERA_HEIGHT, 270); pIRbuf = NULL; pIRbuf = (char *)malloc(IMAGE_SIZE);

创建线程互斥锁以及线程,如下所示。

// 2.创建识别线程,以及图像互斥锁 pthread_mutex_init(&img_lock, NULL); pResult = (Result_t *)malloc(sizeof(Result_t)); memset(pResult, 0, sizeof(Result_t)); if(0 != CreateNormalThread(detect_thread_entry, pResult, &mTid)){ free(pResult); }

初始化显示屏,如下所示。

// 3.显示初始化 ret = disp_init(SCREEN_WIDTH, SCREEN_HEIGHT);

抓取图像,调用clone操作。

// 4.(取流 显示)循环 pthread_mutex_lock(&img_lock); ret = rgbcamera_getframe(pRGBbuf); ret = ircamera_getframe(pIRbuf); algorithm_image = Mat(CAMERA_HEIGHT, CAMERA_WIDTH, CV_8UC3, pRGBbuf); algorithm_IR_image = Mat(CAMERA_HEIGHT, CAMERA_WIDTH, CV_8UC3, pIRbuf); image = algorithm_image.clone(); pthread_mutex_unlock(&img_lock);

调用显示图像,将分析的目标位置通过pResult标记出来。

// 标记人脸框 rectangle(image, Point(pResult->x1, pResult->y1), Point(pResult->x2, pResult->y2), Scalar(pResult->color[0], pResult->color[1], pResult->color[2]), 3); // 显示合成后的图像 disp_commit(image.data, IMAGE_SIZE);

3.4 算法分析子线程

算法分析子线程,主要完成以下操作:

  • 初始化数据库;
  • 启动按键监听子线程,并设置回调;
  • 根据存储标志,判断是否需要清空数据库;
  • 监测是否图像缓冲区是否为空;
  • 不为空时,证明主函数已发送图像数据过来,线程执行图像获取操作;
  • 检测IR人脸位置;
  • 检测RGB人脸位置;
  • 计算IR人脸与RGB人脸的IoU;
  • 利用RGB人脸进行校正和计算特征值;
  • 用计算出的特征值与数据库中存储的特征值进行比较;
  • 根据存储标志判断是否需要插入或更新数据库中的特征值。

初始化数据库。如下所示。

database_init();

启动按键监听子线程,并设置监听回调。如下所示。

keyEvent_init(); set_event_handle(dataBase_opt_handle);

根据存储标志(由按键监听子线程通过监听回调修改),判断是否需要清空数据库。如下所示。

if(g_delete_all_record){ g_delete_all_record = false; // 删除库 database_delete_all_record(); // 重载数据库 peopleNum = database_getData_to_memory(pFaceData); }

监测是否有图像,操作如下所示。

if(algorithm_image.empty() || algorithm_IR_image.empty()) { usleep(5); continue; }

获取图像操作如下所示。

pthread_mutex_lock(&img_lock); irImage = algorithm_IR_image.clone(); image = algorithm_image.clone(); pthread_mutex_unlock(&img_lock);

调用人脸检测函数,得出IR人脸位置算法得到的目标结果记录于detect_result内,如下所示。

// 活体检测,计算出人脸位置 ret = face_detect_run(detect_ctx, irImage, detect_result); irRect.left = (uint32_t)(detect_result[0].box.x); irRect.top = (uint32_t)(detect_result[0].box.y); irRect.right = (uint32_t)(detect_result[0].box.x detect_result[0].box.width); irRect.bottom = (uint32_t)(detect_result[0].box.y detect_result[0].box.height);

调用人脸检测函数,得出RGB人脸位置算法得到的目标结果记录于detect_result内,如下所示。

// 人脸检测,计算出人脸位置 ret = face_detect_run(detect_ctx, image, detect_result); rgbRect.left = (uint32_t)(detect_result[0].box.x); rgbRect.top = (uint32_t)(detect_result[0].box.y); rgbRect.right = (uint32_t)(detect_result[0].box.x detect_result[0].box.width); rgbRect.bottom = (uint32_t)(detect_result[0].box.y detect_result[0].box.height);

计算出IR人脸与RGB人脸的IoU,偏差过大则不继续后面步骤。如下所示。

if(calc_intersect_of_union(irRect, rgbRect) <= 0.5){ // 识别结果数据,复位 memset(pResult, 0 , sizeof(Result_t)); g_input_feature = false; usleep(1000); continue; }

人脸校正和特征值计算。如下所示。

// 人脸校正(从图像中裁出人脸) face_algin = face_alignment(image, points); // 人脸识别,计算特征值 face_recognition_run(recognition_ctx, &face_algin, &face_feature);

从数据库遍历取出特征值,与上一步得出的特征值进行比较。如下所示。

for(face_index = 0; face_index < peopleNum; face_index){ similarity = face_recognition_comparison(face_feature, (float *)((pFaceData face_index)->feature), 512); if(similarity > 0.5) {break;} }

根据存储状态标志,来判断是否需要对数据库进行增加或修改。如下所示。

if(g_input_feature){ g_input_feature = false; // 特征值入库 database_add_record((pFaceData face_index)->id, pResult->nameStr, (char *)face_feature, sizeof(face_feature)); // 重载数据库 peopleNum = database_getData_to_memory(pFaceData); }

存储状态标志,由按键监听子线程通过监听回调进行修改。

3.5 按键监听子线程

按键监听子线程,主要完成以下操作:

  • 打开input事件节点;
  • 阻塞监听input事件;
  • 根据具体动作回发事件类型进监听回调;

打开input事件节点。如下所示。

fp = fopen(KEY_EVENT_PATH, "r");

阻塞监听input事件。如下所示。

fread((void *)&ie, sizeof(ie), 1, fp);

回发事件类型。如下所示。

g_handle(KEY_XXXX);

4. 开发指南

4.1 示例文件&目录结构

Solution git仓库会随着产品迭代更新,不断新增解决方案代码,当前截图只作参考。

4.1.1 Solution git仓库目录介绍

Solution工程构成如下所示,由功能组件easyeai-api和各个解决方案构成。

单个“solu-”开头的目录即为一个解决方案案例,代码内调用“EASY EAI-API”来满足某一实际应用场景的需求。

功能组件的描述如下所示,easyeai-api是经过高度封装的易用性组件接口,便于用户直接调用板卡资源。

功能 组件目录 组件子目录 描述
功能组件 easyeai-api algorithm_api 算法组件
common_api 通用组件
media_api 多媒体组件
netProtocol_api 网络协议组件
peripheral_api 外设硬件组件

4.1.2 人脸识别方案的目录构成

每个解决方案就是一个独立的项目,项目内包含部分如下所示,项目使用cmake构建自动编译部署。

具体介绍如下所示。

组成部分 描述
build.sh 编译脚本,用于管理生成可执行文件后的部署准备工作,用户可自定义shell命令
CMakeLists.txt 工程管理文件,用于组织整个工程结构,指导cmake生成Makefile
include 用于存放第三方应用库、头文件目录等
src 用于存放实现本方案需求的源代码

4.1.3 解决方案可拓展的目录构成

可拓展的目录是指:开发过程中增加某些功能模块,功能代码。增加模式分为两种:

  • 增加已编译的第三方库,在include、libs目录内添加头文件和库文件;
  • 增加用户自定义的功能模块,推荐在src目录内增加;

具体情况如下所示,第三方模块相关的文件由include/3rd_model/xxx.h、libs/3rd_model/xxx.a。自定义的功能模块可参考src/dataBase、src/keyEvent。

4.2 CMakeLists.txt文件解析

4.2.1 编译环境配置部分:

第一部分为配置部分,配置部分如下所示。(获取当前方案目录、配置工具链、提取方案名称):

配置信息如下所示。

配置项 描述
CMake要求版本 cmake_minimum_required函数指定,要求的最低版本
CMAKE_SYSTEM_NAME cmake的系统类型,交叉编译必须
CMAKE_CROSSCOMPILING cmake是否启动交叉编译
cross.camke camke_host_system_information获取平台信息,发现不是armv7l就导入当前平台的交叉编译配置。
project项目名 由project函数指定

4.2.2 easyeai-api配置部分

第二部分是引入我司的功能组件库(针对当前方案进行:配置EASY EAI API头文件目录、库文件目录以及配置库链接参数):

配置信息如下所示。

配置项 描述
api_inc 最终通过target_include_directories函数指定目标包含的头文件路径
link_directories 由link_directories函数指定easyeai-api库所在路径
LINK_LIBRARIES 由LINK_LIBRARIES函数指定easyeai-api库文件

4.2.3 第三方库配置部分

第三部分配置第三方的库(针对当前方案进行:配置第三方头文件目录、库文件目录、配置第三方库链接参数以及配置源码目录):

配置信息如下所示。

配置项 描述
custom_inc 自定义变量custom_inc,最终通过target_include_directories函数指定目标包含的头文件路径,在源码include目录下
link_directories 由link_directories函数指定第三方库所在路径
custom_libs 自定义变量custom_libs,最终通过target_link_libraries函数指定目标引用的库链接参数
aux_source_directory 自定义变量dir_srcs,用于添加工程代码以及自定义的个人代码

例如添加个人库的目录组成方式如下所示。

aux_source_directory的修改方式为:

aux_source_directory(./src ./src/dataBase ./src/keyEvent dir_srcs)

aux_source_directory(./src dir_srcs) aux_source_directory(./src/dataBase dir_srcs) aux_source_directory(./src/keyEvent dir_srcs)

4.2.4 本方案配置部分

第四部分配置项目的编译信息,内容如下所示:

配置项如下所示。

配置项 描述
add_executable 编译结果为${CURRENT_FOLDER}指定,即方案目录名;
编译的源文件为${dir_srcs}指定;
target_include_directories 指定头文件的名字,由${api_inc}与${custom_inc}指定;
target_link_libraries 指定额外的库,例如opencv的库等

4.3 build.sh编译脚本:

4.3.1 路径定位部分

第一部分用于提取目录用于编译操作,内容如下所示:(进入build.sh脚本所在目录,并且提取当前目录绝对路径,提取当前目录名称)

4.3.2 清除编译部分

第二部分清除操作,清除目录为build、Release,内容如下所示:(执行build.sh脚本时,带入了参数“clear”,则清空编译输出)

4.3.3 编译操作

第三部分,编译直接调用cmake,内容如下所示:(重新编译,成部署目录,并把资源自动部署进板卡)

免责声明:本文为转载,非本网原创内容,不代表本网观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

如有疑问请发送邮件至:bangqikeconnect@gmail.com