目录
2.rockx_vi_handle_thread线程的流程框图
3.rockx_vi_handle_thread线程的代码截图
[3.1. 初始化三个rockx模块](#3.1. 初始化三个rockx模块)
[3.2. 读取每一帧VI数据并进行人脸检测](#3.2. 读取每一帧VI数据并进行人脸检测)
[3.3. 对人脸数据过滤并且进行对齐识别](#3.3. 对人脸数据过滤并且进行对齐识别)
[3.4. 遍历map容器的人脸数据并和当前RV1126的人脸数据进行相似度比较](#3.4. 遍历map容器的人脸数据并和当前RV1126的人脸数据进行相似度比较)
[1. begin() 是什么意思?](#1. begin() 是什么意思?)
[2. end() 是什么意思?](#2. end() 是什么意思?)
[3. iter++ 是什么意思?](#3. iter++ 是什么意思?)
[1.3、map 迭代器里的 first 和 second](#1.3、map 迭代器里的 first 和 second)
[1.4、iter->first 和 (*iter).first 是一样的](#1.4、iter->first 和 (*iter).first 是一样的)
1.本章节介绍
本章节主要介绍rockx_vi_handle_thread 线程的工作流程,这个线程最主要的功能是获取第一个VI模块的视频数据,并且使用rockx的人脸检测模块、人脸识别模块、人脸关键点模块对视频流数据进行人脸识别,最后把识别的结果(包括人脸检测区域、人脸识别名称)保存到全局变量里面。
2.rockx_vi_handle_thread线程的流程框图

上图是rockx_vi_handle_thread线程的工作流程。首先要初始化三个rockx模块,分别是人脸检测模块、人脸识别模块、人脸关键点检测模块。然后获取ROCKX的VI模块数据,然后调用rockx_face_detect检测VI数据的人脸区域数据,并保存到全局变量。并判断VI数据是否有人脸,若有人脸则使用rockx_face_filter过滤人脸。



若过滤到人脸,则调用rockx_face_recognize 对RV1126视频人脸进行识别并且提取特征值。然后遍历Map容器的人脸特征值,并且和当前RV1126视频的人脸特征值进行相似度对比,使用的是rockx_face_feature_similarity,若相似度<=1.1则认定为同一个人,并从Map里面通过人脸特征值获取名字,否则就不处理。
3.rockx_vi_handle_thread线程的代码截图
3.1. 初始化三个 rockx 模块

这段代码是初始化rockx的三个模块,首先要使用rockx_create_config 分配rockx_config_t结构体,并使用rockx_add_config 把对应的rockx路径配置进去,在我们的板子里面在**/userdata/rockx_data** 里面,并使用rockx_create 创建三个rockx_handle_t句柄,分别是face_det_handle( 人脸检测模块 ) 、 face_recognize_handle( 人脸识别模块 ) 、 face_5landmarks_handle( 人脸关键点模块 ) 。
3.2. 读取每一帧 VI 数据并进行人脸检测


初始化rockx_image_t结构体,初始化需要传三个值分别是width = WIDTH(1920)、height = HEIGHT(1080)、pixel_format=ROCKX_PIXEL_FORMAT_YUV420SP_NV12。这三个值都需要和VI模块的配置是一样的。
初始化rockx_image_t 后,则需要通过RK_MPI_SYS_GetMediaBuffer获取每一帧VI模块的数据,并把每一帧VI模块的缓冲区和长度传输给rockx_image_t。具体的代码是rv1126_rockx_image.data = (uint8_t *)RK_MPI_MB_GetPtr(mb)(把每一帧VI缓冲区数据赋值到rockx_image_t的data)、rv1126_rockx_image.size = RK_MPI_MB_GetSize(mb)(把每一帧VI大小赋值到rockx_image_t的size)
赋值到rockx_image_t 后**,** 则调用rockx_face_detect 对每一帧的rockx_image_t图像进行人脸检测,并把人脸检测的结果输出到rockx_object_array_t 。 rockx_object_array_t 的内容主要存储的是人脸检测数量和人脸检测区域信息,并调用set_rockx_face_array 把rockx_object_array_t 设置到++全局变量++。
把当前线程刚检测到的人脸结果保存到一个全局变量里,给其他线程使用。
cs
// 开始人脸检测
rockx_ret = rockx_face_detect(face_det_handle, &input_image, &face_array, NULL);
if (rockx_ret != ROCKX_RET_SUCCESS)
{
printf("rockx_face_detect ERROR %d\n", rockx_ret);
}
set_rockx_face_array(face_array);
set_rockx_face_array(face_array);
就是把这个检测结果保存起来。
因为你的代码里另一个线程 show_vi_thread() 会用它:
rockx_object_array_t face_array = get_rockx_face_array();
People people = get_rockx_people();
也就是说:
cs
rockx_vi_handle_thread()
↓
检测人脸,得到 face_array
↓
set_rockx_face_array(face_array)
↓
保存到共享区域
↓
show_vi_thread()
↓
get_rockx_face_array()
↓
拿到人脸框,画矩形框/显示名字
rectangle(rv1126_mat, boundingBox, color, thickness);
所以它相当于一个线程间数据传递接口。
3.3. 对人脸数据过滤并且进行对齐识别

若人脸检测的数量大于0,则认为成功检测到人脸。就对每一帧RV1126图像的人脸图像进行过滤,使用的API是rockx_face_filter 。 过滤完人脸之后,就通过rockx_face_align 进行对齐,最后进行人脸识别提取人脸特征值,使用的API是rockx_face_recognize。
3.4. 遍历 map 容器的人脸数据并和当前 RV1126 的人脸数据进行相似度比较

这一步是遍历map容器的所有数据,找出所有的value人脸特征值,其中key存储的是人脸的名称,value存储的是人脸特征值。然后通过rockx_face_feature_similarity(&database_iter->second,&rv1126_feature, &similarity) 去逐一对比map容器的人脸特征值和当前RV1126的人脸特征值。database_iter->second 指的是Map容器里面的人脸特征值,rv1126_feature指的是RV1126视频流的人脸特征值。
若对比的相似度<=1.0,则代表同一个人,并找出这个人脸特征对应的名字,在Map容器是KEY值。找到名字后,则把名字设置到全局变量里面set_rockx_people。下面是map容器查找和RV1126视频流匹配的图解,它是通过RV1126的人脸特征值和Map的人脸特征值进行对比,然后通过value获取KEY的值。

扩展
1.迭代器

解析:这里的效果是,创建一个类来获取全局的0号 s_thread_map ,然后把map容器拷贝给 database_face_map 方便进行遍历,database_iter 是一个迭代器,用来遍历容器,比对人脸特征
1.1、什么是迭代器?
迭代器可以理解成:容器里的一个"游标"。
它用来指向容器中的某一个元素。
比如你有一个容器:
map<string, int> scores;
scores["zhangsan"] = 90;
scores["lisi"] = 80;
scores["wangwu"] = 70;
这个 map 里面有三条数据:
"zhangsan" -> 90
"lisi" -> 80
"wangwu" -> 70
如果你想一个一个访问它们,就需要一个东西来"指向当前访问到哪一项"。
这个东西就是迭代器:
map<string, int>::iterator iter;
iter 就像一个指针,刚开始可以指向第一项,然后移动到第二项、第三项。
1.2、迭代器的基本用法
最常见的写法是:
map<string, int>::iterator iter;
for (iter = scores.begin(); iter != scores.end(); iter++)
{
cout << iter->first << " = " << iter->second << endl;
}
这段代码会遍历整个 scores。
1. begin() 是什么意思?
scores.begin()
表示指向容器的第一个元素。
比如:
iter = scores.begin();
意思是:让 iter 指向 scores 里面的第一条数据。
2. end() 是什么意思?
scores.end()
表示容器的结束位置。
注意,end() 不是最后一个元素,它是"最后一个元素的后面"。
可以这样理解:
[zhangsan] [lisi] [wangwu] [end]
所以遍历时要写:
iter != scores.end()
意思是:只要还没有走到末尾,就继续循环。
不能访问 end()。
下面这样是错误的:
iter = scores.end();
cout << iter->first << endl; // 错误
因为 end() 不指向有效元素。
3. iter++ 是什么意思?
iter++
表示让迭代器移动到下一个元素。
1.3、map 迭代器里的 first 和 second
map 的每一项都是一对数据:
key -> value
在 C++ 里,这一对数据叫做 pair。
对于:
map<string, int> scores;
每一个元素可以理解成:
pair<string, int>
里面有两个成员:
first
second
其中:
iter->first
表示 key。
iter->second
表示 value。
例如:
map<string, int> scores;
scores["zhangsan"] = 90;
scores["lisi"] = 80;
scores["wangwu"] = 70;
map<string, int>::iterator iter;
for (iter = scores.begin(); iter != scores.end(); iter++)
{
cout << "name = " << iter->first << endl;
cout << "score = " << iter->second << endl;
}
输出逻辑就是:
name = zhangsan
score = 90
name = lisi
score = 80
name = wangwu
score = 70
1.4、iter->first 和 (*iter).first 是一样的
迭代器有点像指针。
所以这两种写法等价:
iter->first
和:
(*iter).first
同理:
iter->second
等价于:
(*iter).second
但是一般都写:
iter->first
iter->second
更简单。
2.rockx_face_filte
函数的定义:这个API主要是过滤人脸,过滤图像中不符合人脸的图像
rockx_ret_t rockx_face_filter(rockx_handle_t handle, rockx_image_t *in_img, rockx_rect_t *in_box, int *is_false_face);
第一个参数:rockx_handle_t的结构体
第二个参数:in_img是输入的图像
第三个参数:in_box人脸检测区域,是一个矩形
第四个参数:is_falas_face判断当前检测的图像是否是人脸,是人脸就等于true,否则false
3.线程参数传递
cs
pthread_t pid;
VI_PROC_PARAM * vi_arg_params = (VI_PROC_PARAM *)malloc(sizeof(VI_PROC_PARAM));
if(vi_arg_params == NULL)
{
printf("malloc venc arg error\n");
free(vi_arg_params);
}
vi_arg_params->viId = 0; // 线程通道号
ret = pthread_create(&pid, NULL, rockx_vi_handle_thread, (void *)vi_arg_params);
if (ret != 0)
{
printf("create camera_venc_thread failed\n");
}
理论上,线程里面应该这样使用:
VI_PROC_PARAM vi_arg = *(VI_PROC_PARAM *)args;
free(args);
src_mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, vi_arg.viId, -1);
这样,外面传:
vi_arg_params->viId = 0;
线程就从 VI 0 取图像。
如果外面传:
vi_arg_params->viId = 1;
线程就从 VI 1 取图像。