news 2026/5/11 3:03:32

Linux_52:ROCKX+RV1126实现1->N人脸识别功能

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux_52:ROCKX+RV1126实现1->N人脸识别功能

目录

1.Rockx+Rv1126实现1->N人脸识别功能大体流程

2.Rockx+Rv1126实现1->N人脸识别功能代码截图

2.1. RV1126模块的初始化并启动VI工作

2.1. 初始化人脸检测和人脸识别的rockx模块

2.2. 读取单张人脸的图像并提取特征值

2.3. 获取每一帧VI视频数据并提取VI数据的人脸特征值

2.4. 对比单张图片的人脸特征值和VI数据的人脸特征值

2.5. 把识别的人脸名称输出到opencv的Text

2.6. 把处理后的数据传输到VENC编码器

2.7.开启get_rockx_face_recg_venc_thread线程获取每一帧VENC数据并且保存到H264

3.代码


1.Rockx+Rv1126实现1->N人脸识别功能大体流程

上图是实现1->N人脸识别流程,首先要初始化RV1126模块初始化,包括VI模块、VENC模块、人脸检测rockx模块、人脸识别rockx模块,初始化模块之后,就要分两个线程做处理。

主流程是先读取单张图片的图像并提取人脸特征值,然后死循环获取VI的码流数据,然后用rockx的人脸检测模块RV1126的VI数据是否有人脸,如果有人脸则调用rockx的人脸识别模块识别出RV1126视频流的人脸数据并且提取出来。然后对比两个人脸的阈值,如果<=1.0,则认定单张人脸图片和RV1126检测的人脸是同一个,否则就不是同一个人,并把数据通过Opencv显示到VI数据,最后把识别后的VI数据传输到VENC编码器里面

get_rockx_face_recg_venc_thread线程主要是获取每一帧的VENC码流数据,并且保存起来。

2.Rockx+Rv1126实现1->N人脸识别功能代码截图

2.1. RV1126模块的初始化并启动VI工作

上图是RV1126模块的初始化,这里需要初始化RV1126的VI模块、VENC模块、并调用RK_MPI_VI_StartStream启动VI模块采集摄像头数据。具体的API不讲解了,因为之前已经讲了很多次。

2.1.初始化人脸检测和人脸识别的rockx模块

上图是rockx模块的初始化,这里我们要初始化两个rockx模块,分别是人脸检测rockx模块和人脸识别rockx模块。首先要使用rockx_create_config分配rockx_config_t结构体,并使用rockx_add_config把对应的rockx路径配置进去,在我们的板子里面在/userdata/rockx_data里面。

使用rockx_create创建人脸检测rockx_handle_t句柄。rockx_create的传参第一个参数rockx_handle_t结构体指针、 第二个参数rockx_module_t是ROCKX_MODULE_FACE_DETECTION_V2ROCKX_MODULE_FACE_DETECTION_V2是人脸检测的Version2模块、第三个参数是rockx_config_t结构体指针、第四个参数默认是0。

使用rockx_create创建人脸识别rockx_handle_t句柄。rockx_create的传参第一个参数rockx_handle_t结构体指针、 第二个参数rockx_module_t是ROCKX_MODULE_FACE_RECOGNIZEROCKX_MODULE_FACE_RECOGNIZE是人脸识别模块、第三个参数是rockx_config_t结构体指针、第四个参数默认是0。

2.2.读取单张人脸的图像并提取特征值

rockx_image_read读取单张人脸图像,face_02.jpg这张图片是我上节课拍照的图片,并且用rockx_face_recognize提取单张图片的人脸特征值,第一个参数传值:人脸识别的句柄(face_reco_handle),第二个参数传值:单张人脸的rockx_image_t数据地址(&single_face_image),第三个参数传值:获取单张人脸的人脸特征(&single_face_feature)

2.3.获取每一帧VI视频数据并提取VI数据的人脸特征值

首先初始化rockx_image_t结构体,包括

width(WIDTH=1920),height(HEIGHT=1080),pixel_format(ROCKX_PIXEL_FORMAT_YUV420SP_NV12)。使用RK_MPI_SYS_GetMediaBuffer获取每一帧VI数据,并且把每一帧VI数据赋值到rockx_image_t这里关键要赋值的是data(data =(uint8_t *)RK_MPI_MB_GetPtr(mb) )和size(size = RK_MPI_MB_GetSize(mb))。

赋值后,就调用rockx_face_detect对每一帧的视频数据进行人脸检测,如果rockx_object_array_t的count大于0(face_detect_array.count)则说明检测到人脸。若检测到人脸则调用rockx_face_recognize提取VI摄像头的人脸特征值,第一个参数传值:人脸识别的句柄(face_reco_handle),第二个参数传值:rv1126的图像数据地址(&rv1126_video_image),第三个参数传值:获取rv1126视频流的人脸特征(&rv1126_face_feature)

2.4.对比单张图片的人脸特征值和VI数据的人脸特征值

提取完单张图片特征值和RV1126视频流的人脸特征值后就要对比两个特征值的相似度了,在rockx框架提供了rockx_face_feature_similarity去对比两个脸特征值。第一个参数传值:单张人脸的特征结构体指针(&single_face_feature), 第二个参数:rv1126视频流人脸特征结构体指针(&rv1126_face_feature),第三个参数:两个人脸对比输出的相似度阈值similarity(&similarity)。

如果输出的人脸相似度值<=1.0(1.0是rockx比较经典的人脸识别阈值, 值越小相似度越高),则说明单张人脸和视频流的人脸是同一个人,然后把”Harry”输出到string , 否则就不输出。

2.5.把识别的人脸名称输出到opencvText

上述步骤已经得到了对比的人脸名称,这一步则需要把人脸的名称输出到OPENCV里面,并显示到视频上。首先要创建OPENCV的矩阵Mat rv1126_mat = Mat(HEIGHT,WIDTH,CV_8UC1,rv1126_video_image.data),第一个参数:HEIGHT(1080),第二个参数HEIGHT(1920),第三个参数:CV_8UC1(单通道),第四个参数:rv1126_video_image.data(每一帧的rv1126VI数据).

创建完矩阵后,则需要调用OPENCV的putText把人脸名称输出到Mat矩阵里面。具体的如下:putText(rv1126_mat,reco_name,text_point,FONT_HERSHEY_COMPLEX,1.0,Scalar(255,0,255),1),第一个参数:rv1126_mat(Mat矩阵数据),第二个参数:reco_name(人脸名称的string字符串),第三个参数:text_point(坐标信息,x=300,y-300),第四个参数:FONT_HERSHEY_COMPLEX(字体类型),第五个参数:1.0(字体大小是1.0),第六个参数:Scalar(255,0,255)(颜色标量),第七个参数:1(粗细程度是1)

2.6.把处理后的数据传输到VENC编码器

上述识别工作完成之后,就需要把VI数据传输到VENC编码器里面。这里直接调用RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, VENC_CHN, mb)去发送。

2.7.开启get_rockx_face_recg_venc_thread线程获取每一帧VENC数据并且保存到H264

上图是创建一个get_rockx_face_recg_venc_thread线程,在这个线程里面通过RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, VENC_CHN, -1)获取每一帧VENC码流,并且用fwrite写到face_reco.h264文件里面。

最终的运行效果:

上图就是程序运行的效果,在这个H264里面,可以识别到我的人脸名称,并且显示出来。

3.代码

/**************************************************************************** * * Copyright (c) 2017 - 2019 by Rockchip Corp. All rights reserved. * * The material in this file is confidential and contains trade secrets * of Rockchip Corporation. This is proprietary information owned by * Rockchip Corporation. No part of this work may be disclosed, * reproduced, copied, transmitted, or used in any way for any purpose, * without the express written permission of Rockchip Corporation. * *****************************************************************************/ #include <assert.h> #include <fcntl.h> #include <getopt.h> #include <opencv2/core/hal/interface.h> #include <opencv2/imgproc.hpp> #include <opencv2/imgproc/imgproc_c.h> #include <pthread.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <string> // #include "common/sample_common.h" #include "rkmedia_api.h" #include "rockx.h" #include <opencv2/core.hpp> // #include <opencv2/imgoroc.hpp> #include <opencv2/highgui.hpp> #include <opencv2/opencv.hpp> #define CAMERA_PATH "rkispp_scale0" #define CAMERA_ID 0 #define CAMERA_CHN 0 #define VENC_CHN 0 #define WIDTH 1920 #define HEIGHT 1080 using namespace std; using namespace cv; string reco_name; void * get_rockx_face_recg_venc_thread(void * args) { pthread_detach(pthread_self()); MEDIA_BUFFER mb = NULL; FILE * face_reco_venc_h264 = fopen("face_reco.h264", "w+"); while (1) { mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, VENC_CHN, -1); if(!mb) { printf("Get Venc Stream break...\n"); break; } fwrite(RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), 1, face_reco_venc_h264); RK_MPI_MB_ReleaseBuffer(mb); } return NULL; } int main(int argc, char **argv) { int ret; VI_CHN_ATTR_S vi_chn_attr; vi_chn_attr.pcVideoNode = CAMERA_PATH; // Path vi_chn_attr.u32Width = 1920; // Width vi_chn_attr.u32Height = 1080; // Height vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12; // ImageType vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // BufType vi_chn_attr.u32BufCnt = 3; // Cnt vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL; // Mode ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, CAMERA_CHN, &vi_chn_attr); if (ret) { printf("Vi Set Attr Failed.....\n"); return 0; } else { printf("Vi Set Attr Success.....\n"); } ret = RK_MPI_VI_EnableChn(CAMERA_ID, CAMERA_CHN); if (ret) { printf("Vi Enable Attr Failed.....\n"); return 0; } else { printf("Vi Enable Attr Success.....\n"); } VENC_CHN_ATTR_S venc_chn_attr; memset(&venc_chn_attr, 0, sizeof(VENC_CHN_ATTR_S)); venc_chn_attr.stVencAttr.u32PicWidth = 1920; venc_chn_attr.stVencAttr.u32PicHeight = 1080; venc_chn_attr.stVencAttr.u32VirWidth = 1920; venc_chn_attr.stVencAttr.u32VirHeight = 1080; venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12; venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264; venc_chn_attr.stVencAttr.u32Profile = 66; venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 25; venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = 1920 * 1080 * 3; venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25; venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr); if (ret) { printf("ERROR: Create venc failed!\n"); exit(0); } ret = RK_MPI_VI_StartStream(CAMERA_ID, CAMERA_CHN); if (ret) { printf("ERROR: RK_MPI_VI_StartStream failed!\n"); exit(0); } rockx_config_t * face_config = rockx_create_config(); rockx_add_config(face_config, ROCKX_CONFIG_DATA_PATH, "/userdata/rockx_data"); rockx_handle_t face_det_handle; rockx_module_t face_det_module = ROCKX_MODULE_FACE_DETECTION_V2; rockx_ret_t face_det_ret; face_det_ret = rockx_create(&face_det_handle, face_det_module, face_config, 0); if(face_det_ret != ROCKX_RET_SUCCESS) { printf("rockx_create face_detect_handle failed!\n"); return -1; } rockx_handle_t face_reco_handle; rockx_module_t face_reco_module = ROCKX_MODULE_FACE_RECOGNIZE; rockx_ret_t face_recg_ret; face_recg_ret = rockx_create(&face_reco_handle, face_reco_module, face_config, 0); if(face_recg_ret != ROCKX_RET_SUCCESS) { printf("rockx_create face_recognize_handle failed!\n"); return -1; } char * image_path = "face_02.jpg"; rockx_image_t single_face_image; rockx_image_read(image_path, &single_face_image, 1); rockx_face_feature_t single_face_feature; rockx_face_recognize(face_reco_handle, &single_face_image,&single_face_feature); pthread_t pid; pthread_create(&pid, NULL, get_rockx_face_recg_venc_thread, NULL); MEDIA_BUFFER mb = NULL; rockx_image_t rv1126_video_image; rv1126_video_image.width = WIDTH; rv1126_video_image.height = HEIGHT; rv1126_video_image.pixel_format = ROCKX_PIXEL_FORMAT_YUV420SP_NV12; rockx_ret_t sim_ret; Point text_point; text_point.x = 300; text_point.y = 300; while (1) { mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, CAMERA_CHN, -1); if(!mb) { printf("Get vi data break....\n"); break; } rv1126_video_image.data = (uint8_t *)RK_MPI_MB_GetPtr(mb); rv1126_video_image.size = RK_MPI_MB_GetSize(mb); Mat rv1126_mat = Mat(HEIGHT,WIDTH,CV_8UC1,rv1126_video_image.data); rockx_object_array_t face_detect_array; face_det_ret = rockx_face_detect(face_det_handle,&rv1126_video_image,&face_detect_array,NULL); if(face_det_ret != ROCKX_RET_SUCCESS) { printf("rockx_face_detect failed!.....\n"); } rockx_face_feature_t rv1126_face_feature; if(face_detect_array.count > 0) { face_recg_ret = rockx_face_recognize(face_reco_handle,&rv1126_video_image,&rv1126_face_feature); if(face_recg_ret != ROCKX_RET_SUCCESS) { printf("rockx_face_recognize failed....\n"); } float similarity; sim_ret = rockx_face_feature_similarity(&single_face_feature, &rv1126_face_feature, &similarity); if(sim_ret != ROCKX_RET_SUCCESS) { printf("rockx_face_feature_similarity failed....\n"); } if(similarity <= 1.0) { reco_name = "Harry"; printf("This is Harry...\n"); } else { reco_name = ""; printf("Can not recognize...\n"); } putText(rv1126_mat,reco_name,text_point,FONT_HERSHEY_COMPLEX,1.0,Scalar(255,0,255),1); } RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, VENC_CHN, mb); RK_MPI_MB_ReleaseBuffer(mb); } RK_MPI_VI_DisableChn(CAMERA_ID,CAMERA_CHN); RK_MPI_VENC_DestroyChn(VENC_CHN); return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/11 3:01:36

二手电车处处是坑,坐实快消品的名号,买电车只应买低价车

随着电车存量达到一定规模&#xff0c;如今不少电车已进入二手市场&#xff0c;但是二手电车的坑实在太多了&#xff0c;业界人士披露二手电车的诸多大坑&#xff0c;这可能导致电车难以在二手市场卖出&#xff0c;而成为真正的快消品&#xff0c;反过来影响电车市场。与燃油车…

作者头像 李华
网站建设 2026/5/11 3:00:30

AI API智能调度中继服务:多账号管理与高可用架构实践

1. 项目概述&#xff1a;一个高性能的AI API智能调度中转站如果你手头有多个Claude、Gemini或者OpenAI的账号&#xff0c;并且经常在不同的开发工具&#xff08;比如Claude Code CLI、各种SDK&#xff09;之间切换使用&#xff0c;那你肯定体会过那种管理上的繁琐。每次调用都得…

作者头像 李华
网站建设 2026/5/11 2:59:39

星露谷物语模组加载器SMAPI:免费开源的游戏增强终极指南

星露谷物语模组加载器SMAPI&#xff1a;免费开源的游戏增强终极指南 【免费下载链接】SMAPI The modding API for Stardew Valley. 项目地址: https://gitcode.com/gh_mirrors/smap/SMAPI 星露谷物语模组加载器SMAPI是《星露谷物语》的官方模组API&#xff0c;为这款经典…

作者头像 李华
网站建设 2026/5/11 2:59:39

FPGA中AXI-FIFO主机接口的自定义实现与versal读写工程分析

AXI-FIFO主机接口实现解析工程架构 核心模块包括top.v、mem_test.v和aq_axi_master.v。aq_axi_master模块将AXI协议转换为类FIFO的本地总线接口&#xff0c;用户逻辑通过简单的FIFO操作即可完成DDR4读写&#xff0c;无需直接处理AXI协议细节。本地总线接口设计控制接口&#xf…

作者头像 李华
网站建设 2026/5/11 2:50:15

现代前端工程化实践:从项目模板到自动化工作流

1. 项目概述与核心价值最近在整理自己的开源项目时&#xff0c;我一直在思考一个问题&#xff1a;如何能让一个项目从“能用”变得“好用”&#xff0c;并且让后续的维护和协作变得轻松&#xff1f;这不仅仅是写几行代码、加个README那么简单。它涉及到项目结构的规范性、开发流…

作者头像 李华