Linux58:rockx_vi_handle_thread线程的讲解

目录

1.本章节介绍

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.迭代器

1.1、什么是迭代器?

1.2、迭代器的基本用法

[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 是一样的)

2.rockx_face_filte

3.线程参数传递


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_arrayrockx_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 迭代器里的 firstsecond

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 取图像。

相关推荐
七夜zippoe1 小时前
OpenClaw Browser 自动化:表单填写实战
服务器·自动化·表单·browser·openclaw
袁煦丞 cpolar内网穿透实验室2 小时前
出差路上,服务器在我手机里
运维·服务器·docker·容器·智能手机·远程工作·cpolar
小此方2 小时前
Re:Linux系统篇(十三)特别篇: 实现Linux第⼀个系统程序−进度条
linux·运维·服务器
tedcloud1237 小时前
UI-TARS-desktop部署教程:构建AI桌面自动化系统
服务器·前端·人工智能·ui·自动化·github
AC赳赳老秦10 小时前
供应链专员提效:OpenClaw自动跟踪物流信息、更新库存数据,异常自动提醒
java·大数据·服务器·数据库·人工智能·自动化·openclaw
夏日听雨眠10 小时前
LInux(逻辑地址与物理地址的区别,文件描述符,lseek函数)
linux·运维·网络
哲霖软件11 小时前
ERP 赋能非标自动化行业:破解物料与库存管理难题
运维·自动化
qq_5425154112 小时前
Ubuntu 22.04.4 LTS安装ToDesk最新版打不开,无响应?旧版本4.7.2_277版本分享
linux·ubuntu·todesk
火车叼位12 小时前
替代 Tiny Win10 的 Linux 方案:Debian XFCE 精简桌面搭建
linux·运维