人脸识别AI项目(二)
本项目主要呈现的功能是通过采集摄像头的视频数据,并通过人脸识别把名称显示到VI数据,并推送到流媒体服务器上面。这个项目运用的功能很多,包括了rockx人脸检测、识别功能,sqlite3数据库存储人脸功能,rkmedia的VENC编码功能,rkmedia的VI采集功能,ffmpeg推流、OPENCV画框描绘文字功能。这个项目大部分功能都和第一阶段的功能点很像,包括vi采集数据、VENC编码、 FFM
3.远程人脸AI监控项目
3.1ROCKX+RV1126人脸识别推流项目
本项目主要呈现的功能是通过采集摄像头的视频数据,并通过人脸识别把名称显示到VI数据,并推送到流媒体服务器上面。这个项目运用的功能很多,包括了rockx人脸检测、识别功能,sqlite3数据库存储人脸功能,rkmedia的VENC编码功能,rkmedia的VI采集功能,ffmpeg推流、OPENCV画框描绘文字功能。
这个项目大部分功能都和第一阶段的功能点很像,包括vi采集数据、VENC编码、 FFMPEG推流等功能。所以我们这个项目的特色是在第一阶段推流的基础上添加了rockx识别VI摄像头的人脸识别、sqlite3存储人脸特征值、对比数据库人脸特征和摄像头的人脸数据特征、OPENCV画框描绘文字等功能。
程序的大体框图

上图是rv1126+rockx人脸识别推流的大体框图,这个项目首先要初始化模块和队列容器。模块包括两个VI模块,第一个VI模块是用于ROCKX的人脸检测、人脸识别的AI推理,第二个VI模块用于显示推理结果。(AI处理消耗cpu资源,要用另一个VI处理,不然会数据卡顿)除了模块外,还需要初始化容器和队列,容器包括Map容器(主要是存储人脸数据库的特征值和人脸名称)、视频编码队列。
初始化所有东西后,则需要使用四个线程去处理,分别是rockx_vi_handle_thread线程、show_vi_thread线程、camera_venc_thread线程、video_push_thread线程。rockx_vi_handle_thread线程主要功能是读取第一个模块的VI数据,并使用rockx框架对VI数据进行人脸检测和人脸识别:当检测到人脸数据后,先进行人脸过滤,然后再调用人脸识别API对其进行人脸特征值的提取,并把摄像头提取的人脸特征和数据库(Map容器)的人脸特征进行对比,若相似度<=1.2则认为是同一个人,若识别到同一个人则输出这个人的名字。
show_vi_thread线程主要是获取第二个VI模块的摄像头数据,然后获取rockx_vi_handle_thread线程的人脸坐标(在第一个线程中获取)和人脸名称(在Map中获取),并用opencv显示到第二个VI模块里面,然后把处理后的VI数据推送到VENC编码器里面。
camera_venc_thread线程主要是获取VENC编码器的数据,并把每一帧的VENC码流数据存放到H264的VIDEO_QUEUE的视频队列。
video_push_thread线程从VIDEO_QUEUE的视频队列获取每一帧的视频编码数据,然后再利用FFMPEG流媒体推流框架,把视频编码数据传输到流媒体服务器(主要是RTMP服务器)。
三.本项目程序的思维导图

上图是rockx+1126人脸识别的程序思维导图框图,这个项目的main函数是整个项目的入口函数。在这里入口函数里面,需要做四个比较重要的步骤:分别是init_rkmedia_module_function初始化rkmdia的组件、初始化编码视频队列HIGH_VIDEO_QUEUE、init_face_data初始化数据库的人脸数据、init_rv1126_first_assignment开启RV1126的人脸识别推流任务。
2.1. init_rkmedia_module_function初始化组件:
这个函数主要是做RKMEDIA的组件初始化,组件包括:AI推理的VI模块的初始化、视频显示的VI模块初始化、VENC模块的初始化
2.1.1. AI推理模块的VI模块初始化:这个VI模块主要是用于rockx的人脸检测、人脸识别的推理
2.1.2.显示的VI模块初始化:这个VI模块主要是用于显示rockx处理的结果,包括人脸检测的坐标信息和人脸识别后的名称
2.1.3.VENC模块的初始化:初始化H264的编码模块
2.2. 初始化HIGH_VIDEO_QUEUE:
初始化1920 * 1080分辨率编码数据队列,这个队列主要是存储1920 * 1080编码的视频数据,主要是用于多线程处理
2.3. init_face_data初始化数据库的人脸数据:
查询sqlite3人脸数据库的人脸特征值和姓名,并通过map容器进行key->value存储,key保存人的名称、value保存的是人脸特征值
2.4. init_rv1126_first_assignment开启RV1126的人脸识别推流任务:
这个函数里面有五个步骤,分别是init_rkmedia_ffmpeg_context初始化1920 * 1080分辨率的ffmpeg推流器、创建rockx_vi_handle_thread线程、创建show_vi_handle_thread线程、创建camera_venc_thread线程、创建high_video_push_thread线程。
2.4.1. init_rkmedia_ffmpeg_context初始化1920 * 1080的推流器
在这个函数里面主要是对FFMPEG推流器参数进行设置,它需要对高分辨率(1920 * 1080)的FFMPEG推流器进行初始化。
2.4.2.创建rockx_vi_handle_thread线程
这个线程的作用是通过rockx框架对VI数据进行人脸检测、人脸识别处理,然后把人脸检测的坐标信息和人脸识别的名称保存起来。
2.4.3.创建show_vi_handle_thread线程
这个线程的作用获取显示VI的摄像头数据,然后获取rockx_vi_handle_thread线程处理的坐标信息和人脸名称,并用opencv显示上去,然后把处理后的VI数据发送到VENC编码器。
2.4.4.创建camera_venc_thread线程
这个线程主要功能是获取VENC编码器的码流数据,并且把VENC的码流数据存放到high_video_queue的视频编码队列里面,high_video_queue视频队列主要是为了推流线程用的。
2.4.5.创建video_push_thread线程
这个线程主要功能是获取high_video_queue队列的视频编码数据,并利用FFMPEG流媒体框架把H264码流推送到RTMP流媒体服务器。
3.2 RV1126的VI模块讲解
一.介绍:
介绍rockx+rv1126人脸识别推流项目的VI模块的初始化,这个项目的VI模块需要初始化两个,一个是用于rockx的人脸检测、人脸识别的处理。另外一个则用于显示AI处理结果。设置VI模块的代码在rkmedia_module_function.cpp里面。
二.VI模块的思维导图:

上面思维导图主要是描述VI模块设置的大致流程,这里要创建两个RV1126_VI_CONFIG结构体并进行参数设置,第一个是用于rockx人脸检测、识别,第二个是用于显示AI处理结果。设置完参数之后,则需要调用rkmedia_vi_init去初始化这两个VI模块并且使能,最后把这两个VI模块的ID号存放到数组里面(vi_containers)。
VI模块代码的截图
3.1. rockx的VI模块设置

id:VI模块的id号,用于初始化和使能VI模块
pcVideoNode: 摄像头的视频节点,这里默认是rkispp_scale0
u32BufCnt:缓冲区计数,默认是3
u32Width:VI模块分辨率宽度1920
u32Height:VI模块分辨率高度1080
enPixFmt:图像格式默认是NV12,这里填的是IMAGE_TYPE_NV12
enBufType:VI模块捕捉视频的类型,这里默认填写MMAP
enWorkMode:VI工作模式,这里写的是VI_WORK_MODE_NORMAL
设置完上述参数后,则需要调用set_vi_container把VI的模块号放到数组里面,这里数组的索引号是0。
3.2.显示AI结果的VI模块设置

这部分的参数跟上面大部分是相同的,区别在于pcVideoNode。rockx VI模块的pcVideoNode是rkispp_scale0,而显示VI模块的pcVideoNode是rkispp_scale1,下面我们来看具体的参数设置。
id:VI模块的id号,用于初始化和使能VI模块
pcVideoNode: 摄像头的视频节点,这里选择的是rkispp_scale1
u32BufCnt:缓冲区计数,默认是3
u32Width:VI模块分辨率宽度1920
u32Height:VI模块分辨率高度1080
enPixFmt:图像格式默认是NV12,这里填的是IMAGE_TYPE_NV12
enBufType:VI模块捕捉视频的类型,这里默认填写MMAP
enWorkMode:VI工作模式,这里写的是VI_WORK_MODE_NORMAL
设置完上述参数后,则需要调用set_vi_container把VI的模块号放到数组里面,这里数组的索引号是1。
3.3. 启动两个VI模块进行视频采集

设置完两个VI模块的参数之后,就需要启动模块进行视频数据的采集,这里直接调用RK_MPI_VI_StartStream去开启摄像头的两个视频模块进行采集。
3.4. rkmedia_vi_init函数的实现:

rkmedia_vi_init这个自定义函数里面,关键是对VI进行初始化和使能,它分别调用了RK_MPI_VI_SetChnAttr的API对VI模块的属性进行初始化,然后再调用RK_MPI_VI_EnableChn对其使能。
3.5. set_vi_container函数的实现:

上面是set_vi_container函数的实现,这里要把VI的模块号存放到RV1126_VI_CONTAINTER结构体容器里面。

源码 rkmedia_module_function.cpp
#include "rkmedia_module_function.h"
#include "rkmedia_assignment_manage.h"
#include "rkmedia_config_public.h"
#include "rkmedia_module.h"
#include "rkmedia_container.h"
#include "SDL.h"
#include "SDL_ttf.h"
#include <sys/time.h>
#define FILE_IMAGE_LENGTH (64 * 1024)
static int get_align16_value(int input_value, int align)
{
int handle_value = 0;
if (align && (input_value % align))
handle_value = (input_value / align + 1) * align;
return handle_value;
}
int read_image(char *filename, char *buffer)
{
if (filename == NULL || buffer == NULL)
return -1;
FILE *fp = fopen(filename, "rb"); // 以二进制模式读取该文件
if (fp == NULL)
{
printf("fopen failed\n");
return -2;
}
// 检测文件大小file
fseek(fp, 0, SEEK_END);
int length = ftell(fp);
fseek(fp, 0, SEEK_SET);
int size = fread(buffer, 1, length, fp);
if (size != length)
{
printf("fread failed:%d\n", size);
return -3;
}
fclose(fp);
return size;
}
static int get_cur_time_ms(void)
{
struct timeval tv;
gettimeofday(&tv, NULL); // 使用gettimeofday获取当前系统时间
return (tv.tv_sec * 1000 + tv.tv_usec / 1000); // 利用struct timeval结构体将时间转换为ms
}
int init_rkmedia_module_function()
{
rkmedia_function_init();
RV1126_VI_CONFIG rockx_rkmedia_vi_config;
memset(&rockx_rkmedia_vi_config, 0, sizeof(rockx_rkmedia_vi_config));
rockx_rkmedia_vi_config.id = 0;
rockx_rkmedia_vi_config.attr.pcVideoNode = AI_CMOS_DEVICE_NAME; // VIDEO视频节点路径,
rockx_rkmedia_vi_config.attr.u32BufCnt = 3; // VI捕获视频缓冲区计数,默认是3
rockx_rkmedia_vi_config.attr.u32Width = 1920; // 视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
rockx_rkmedia_vi_config.attr.u32Height = 1080; // 视频输入的高度,一般和CMOS摄像头或者外设的高度一致
rockx_rkmedia_vi_config.attr.enPixFmt = IMAGE_TYPE_NV12; // 视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
rockx_rkmedia_vi_config.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // VI捕捉视频的类型
rockx_rkmedia_vi_config.attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
int ret = rkmedia_vi_init(&rockx_rkmedia_vi_config); // 初始化VI工作
if (ret != 0)
{
printf("rockx_vi init error\n");
}
else
{
printf("rockx_vi init success\n");
RV1126_VI_CONTAINTER vi_container;
vi_container.id = 0;
vi_container.vi_id = rockx_rkmedia_vi_config.id;
set_vi_container(0, &vi_container); // 设置VI容器
}
RV1126_VI_CONFIG show_rkmedia_vi_config;
memset(&show_rkmedia_vi_config, 0, sizeof(show_rkmedia_vi_config));
show_rkmedia_vi_config.id = 1;
show_rkmedia_vi_config.attr.pcVideoNode = SHOW_CMOS_DEVICE_NAME; // VIDEO视频节点路径,
show_rkmedia_vi_config.attr.u32BufCnt = 3; // VI捕获视频缓冲区计数,默认是3
show_rkmedia_vi_config.attr.u32Width = 1920; // 视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
show_rkmedia_vi_config.attr.u32Height = 1080; // 视频输入的高度,一般和CMOS摄像头或者外设的高度一致
show_rkmedia_vi_config.attr.enPixFmt = IMAGE_TYPE_NV12; // 视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
show_rkmedia_vi_config.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // VI捕捉视频的类型
show_rkmedia_vi_config.attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
ret = rkmedia_vi_init(&show_rkmedia_vi_config); // 初始化VI工作
if (ret != 0)
{
printf("show_vi init error\n");
}
else
{
printf("show_vi init success\n");
RV1126_VI_CONTAINTER vi_container;
vi_container.id = 1;
vi_container.vi_id = show_rkmedia_vi_config.id;
set_vi_container(1, &vi_container); // 设置VI容器
}
RV1126_VENC_CONFIG rkmedia_venc_config = {0};
memset(&rkmedia_venc_config, 0, sizeof(rkmedia_venc_config));
rkmedia_venc_config.id = 0;
rkmedia_venc_config.attr.stVencAttr.enType = RK_CODEC_TYPE_H264; // 编码器协议类型
rkmedia_venc_config.attr.stVencAttr.imageType = IMAGE_TYPE_NV12; // 输入图像类型
rkmedia_venc_config.attr.stVencAttr.u32PicWidth = 1920; // 编码图像宽度
rkmedia_venc_config.attr.stVencAttr.u32PicHeight = 1080; // 编码图像高度
rkmedia_venc_config.attr.stVencAttr.u32VirWidth = 1920; // 编码图像虚宽度,一般来说u32VirWidth和u32PicWidth是一致的
rkmedia_venc_config.attr.stVencAttr.u32VirHeight = 1080; // 编码图像虚高度,一般来说u32VirHeight和u32PicHeight是一致的
rkmedia_venc_config.attr.stVencAttr.u32Profile = 66; // 编码等级H.264: 66: Baseline; 77:Main Profile; 100:High Profile; H.265: default:Main; Jpege/MJpege: default:Baseline(编码等级的作用主要是改变画面质量,66的画面质量最差利于网络传输,100的质量最好)
rkmedia_venc_config.attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; // 编码器码率控制模式
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32Gop = 25; // GOPSIZE:关键帧间隔
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32BitRate = 1920 * 1080 * 3; // 码率
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; // 目的帧率分子:填的是1固定
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25; // 目的帧率分母:填的是25固定
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; // 源头帧率分子:填的是1固定
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; // 源头帧率分母:填的是25固定
ret = rkmedia_venc_init(&rkmedia_venc_config); // VENC模块的初始化
if (ret != 0)
{
printf("venc init error\n");
}
else
{
RV1126_VENC_CONTAINER venc_container;
venc_container.id = 0;
venc_container.venc_id = rkmedia_venc_config.id;
set_venc_container(0, &venc_container);
printf("venc init success\n");
}
ret = RK_MPI_VI_StartStream(CAMERA_ID, 0);
if(ret)
{
printf("RK_MPI_VI_StartStream_0 Failed....\n");
}
ret = RK_MPI_VI_StartStream(CAMERA_ID, 1);
if(ret)
{
printf("RK_MPI_VI_StartStream_1 Failed....\n");
}
return 0;
}
3.3RV1126的VENC模块讲解
介绍rockx+rv1126项目的VENC编码模块,在这个项目中VENC编码模块参数的设置是至关重要的,它可以对VI数据进行硬件编码让其可以进行编码码流的推流,VENC模块的配置在源文件rkmedia_module_function.cpp里面。
VENC模块的思维导图
![]()
上面思维导图主要是描述VENC模块设置的大致流程,这里要创建RV1126_VENC_CONFIG结构体并进行参数设置,设置完参数之后,则需要调用rkmedia_venc_init去初始化这两个VENC模块,最后把VENC的ID号存放到数组里面(venc_containers)。
VENC模块设置的代码截图
3.1. venc参数的设置

上述的参数是高分辨率VENC编码器的参数设置,我们来看看每个参数设置的意义
stVencAttr.enType:编码器协议类型,这里填写的是H264编码器类型,RK_CODEC_TYPE_H264
stVencAttr.imageType:编码器图像类型,这里写的类型要和图像输入类型保持一致,所以填写IMAGE_TYPE_NV12
stVencAttr.enType.u32PicWidth:编码图像的分辨率宽度,这里是高分辨率所以写1920
stVencAttr.enType.u32PicHeight:编码图像的分辨率高度,这里是高分辨率所以写1080
stVencAttr.enType.u32VirWidth:编码图像的分辨率虚宽,这里写的跟u32PicWidth保持一致,所以填写1920
stVencAttr.enType.u32VirHeight:编码图像的分辨率虚高,这里写的跟u32PicHeight保持一致,所以填写1080
stVencAttr.enType.u32Profile:编码等级,这里填写66,指的是Baseline,这种模式更加适用于视频传输
stRcAttr.enRcMode:编码器码率控制类型,这里填写的是H264 CBR码率控制模式,VENC_RC_MODE_H264CBR,下面码率控制的结构体都是以stH264Cbr作为设置
stRcAttr.stH264Cbr.u32Gop:H264的CBR码率控制GOP设置,这里GOP的设置是25
stRcAttr.stH264Cbr.u32BitRate:H264的CBR码率控制码率大小的设置,这里是1920 * 1080 * 3 = 6220800比特率/s ~= 700KB/s
stRcAttr.stH264Cbr.u32SrcFrameRateDen:H264的CBR码率控制控制结构体的源帧率分母的设置,这里写的是25
stRcAttr.stH264Cbr.u32SrcFrameRateNum:H264的CBR码率控制控制结构体的源帧率分子的设置,这里写的是1
stRcAttr.stH264Cbr.u32DstFrameRateDen:H264的CBR码率控制控制结构体的目标帧率分母的设置,这里写的是25
stRcAttr.stH264Cbr.u32DstFrameRateNum:H264的CBR码率控制控制结构体的目标帧率分子的设置,这里写的是1
(备注:通常情况下源帧率的分子、分母和目标帧率的分子、分母数值保持一致)
设置完上述VENC编码参数后,我们就要调用自己封装的函数rkmedia_venc_init函数,对VENC模块进行设置,具体的实现如
3.2.rkmedia_venc_init的实现

这个自定义函数还是非常简单的,就是把RK_MPI_VENC_CreateChn封装了一层,然后把RV1126_VENC_CONFIG的结构体指针传进去。
3.3.set_venc_container函数的实现:

设置完VENC模块后,就要把VENC模块的ID号设置到VENC容器数组里面,高分辨率VENC的ID号是0,调用自己封装的函数是set_venc_container。在这个自定义的函数里面,最主要是把VENC的ID号存放在VENC模块数组里面(venc_containers),具体结构如下图。

3.4读取人脸图片并把特征值保存到sqlite3数据库
介绍如何通过rockx读取人脸图像并读取人脸特征值保存到sqlite3数据库里面,在真正的开发中人脸特征值都会存储到数据库里面,一般的数据库有sqlite3、mysql等等。(关于数据库的内容,本项目不做一个详细的介绍,这边会带着大家做一个比较简单的数据库的增删改查操作)。
rockx读取人脸特征值保存数据库大体框图

上图是读取人脸特征和图片数据写到数据库的流程。第一步:要连接和读取人脸的sqlite3数据库。第二步:创建三个rockx句柄,包括人脸检测句柄(face_det_handle)、人脸识别句柄(face_recognize_handle)、人脸关键点句柄(face_5landmarks_handle); 第三步:使用rockx_image_read读取人脸图片; 第四步:使用FILE的API来获取图片的长度和二进制的图片数据; 第五步:使用rockx_face_align对齐人脸图片;第六步:使用rockx_face_recognize提取人脸的特征值;第七步:把人脸特征值和图片二进制数据保存到sqlite3数据库。
rockx读取人脸特征值保存数据库的代码截图
3.1.Connection_sqlite3DataBase连接数据库

这里封装了一个了SQLITE3连接数据库的函数Connection_sqlite3Database,这个函数的实现如下图

这个函数直接调用了sqlite3的api sqlite3_open来初始化人脸数据库,若返回值不等于SQLITE_OK则初始化数据库失败,否则就初始化成功。
3.2.创建三个rockx句柄

这里需要通过rockx_create创建三个句柄来完成人脸检测和人脸识别,分别是
人脸检测句柄(ROCKX_MODULE_FACE_DETECTION_V2)、
人脸识别句柄(ROCKX_MODULE_FACE_RECOGNIZE)、
人脸关键点检测句柄(ROCKX_MODULE_FACE_LANDMARK_5)。
3.3.读取人脸图片

上图是读取人脸的图片,这里使用的是rockx_image_read来读取人脸图片,其中input_image是需要从命令行输入的。
3.4.使用FILE读取图片的二进制数据和长度

上面的代码是读取图片的数据和长度,这里直接使用的是系统的API。通过ftell来获取图片的长度,并使用fread来读取图片的二进制数据。
3.5.使用rockx_face_detect检测人脸数据并找到最精确的人脸

使用rockx_face_detect检测图片的人脸数据,并找到最精确的人脸。具体的查找过程在get_max_face里面,我们来看看这个的实现。

get_max_face的实现是循环人脸的数量,并计算当前人脸的面积(cur_face_box_area)和最大人脸面积(max_face_box_area)。如果当前人脸的面积(cur_face_box_area)>最大人脸面积(max_face_box_area),则把当前人脸面积赋值给最大人脸面积,max_face就是最精确的人脸数据了
3.6. 对齐人脸数据并且提取人脸特征值

找到最精确的人脸数据后,就需要把人脸进行对齐,对齐的API是rockx_face_align。对齐了人脸数据后,就使用rockx_face_recognize来识别图片的人脸数据并提取特征值。
3.7. 把人脸特征值和图片数据保存到sqlite3数据库

![]()
(图二)
最后一步就是把人脸特征值和图片数据保存到sqlite3数据库,要保存数据库要用到sql语句。这里用到的sql语句是
”insert into face_data_table(name,face_feature,feature_size,image_data,image_size) values (?,?,?,?,?);”, insert语句是经典的sql添加数据的语句, 这其中”face_data_table”是我们需要查询的人脸数据表格,它包含了name(人脸名称)、face_feature(人脸特征值)、feature_size(特征值长度)、image_data(图片数据)、image_size(图片长度)(人脸表格数据如图二)。
sqlite3_prepare函数将SQL语句编译成可执行的字节码,从而进行各种SQL的操作,包括: 查询、插入、更新、 删除。
sqlite3_bind_text(stmt, 1, name, strlen(name), NULL):将name绑定到该位置,我们可以执行该预处理语句以将name插入到数据库中。
sqlite3_bind_blob(stmt, 2, feature, featureSize, NULL):将feature绑定到该位置,我们可以执行该预处理语句以将人脸特征值(feature)插入到数据库中。
sqlite3_bind_int(stmt, 3, featureSize):将feature_size绑定到该位置,我们可以执行该预处理语句以将人脸特征值长度(feature_size)插入到数据库中。
sqlite3_bind_blob(stmt, 4, image_data, image_length, NULL):将image_data绑定到该位置,我们可以执行该预处理语句以将图片数据(image_data)插入到数据库中。
sqlite3_bind_int(stmt, 5, image_length):将image_size绑定到该位置,我们可以执行该预处理语句以将图片长度(image_size)插入到数据库中。
在板子上查询数据库的人脸数据
4.1. 打开sqlite3数据库

使用sqlite3 face.db打开人脸数据库
4.2. 查询人脸的数据

使用SQL的查询语句查询人脸识别数据,使用的sql语句是select * from face_data_table,查询的结果就是上面截图。
3.5读取人脸数据库并保存到map
通过查询人脸数据库数据库表格把数据存储到map容器,map容器底层是内存处理,用map去处理数据能够达到高效,快速查询的效果。在这个项目里面,map起到了快速查询的功能,key存储的是人脸的结构体(People),value存储的是人脸具体的数据(rockx_face_feature_t)。人脸特征值比较大,频繁调用数据库完全不可取,数据卡顿,非常耗时。
- 读取数据库保存到map流程框图

上图是读取数据库并加载到Map的过程,分别有三步:第一步,加载并连接sqlite3数据库、第二步:通过select查询人脸表格的数据、第三步:把表格的数据循环插入到map容器。
- 读取数据库保存到map的代码截图

init_face_data是读取数据库数据并插入到map容器的自定义函数,这里有几个重要的步骤分别是加载数据库(Connection_sqlite3DataBase)、查询数据库信息并存放到Map容器(QueryPeopleData)、把Map存放到全局变量里面(set_thread_map)
3.1. 加载并连接sqlite3数据库

这里封装了一个了SQLITE3连接数据库的函数Connection_sqlite3Database,这个函数的实现如下图

这个函数直接调用了sqlite3的api sqlite3_open来初始化人脸数据库,若返回值不等于SQLITE_OK则初始化数据库失败,否则就初始化成功。
3.2. 通过select查询人脸表格的数据

上图是通过sql语句查询人脸表格,”select name, feature_size, face_feature, image_size, image_data from face_data_table”, 其中name: 名字、feature_size:特征值长度、face_feature: 人脸特征数据、image_size:图片长度、image_data:图片数据。face_data_table则是存储人脸的数据库表格。并使用sqlite3_prepare进行sql的预处理。
3.3. 读取查询的结果并循环插入map容器

上图是读取sqlite3数据库的数据,并循环插入到map容器。这里的关键是使用了sqlite3提供的函数sqlite3_step来获取select的结果,若结果等于SQLITE_ROW(表示当前返回结果有数据)则表示有数据,就获取每一行的数据库数据。
sqlite3_column_text(stmt, 0):表示的是查找select语句的第一个元素name,索引号是0,类型是字符串,并进行绑定。
sqlite3_column_int(stmt, 1): 表示的是查找select语句的第二个元素feature_size,索引号是1,类型是int, 并进行绑定。
sqlite3_column_blob(stmt, 2):表示的是查找select语句的第三个元素face_feature,索引号是2,类型是blob, 并进行绑定。
sqlite3_column_int(stmt, 3):表示的是查找select语句的第四个元素image_size,索引号是3,类型是int, 并进行绑定。
sqlite3_column_blob(stmt, 4):表示的是查找select语句的第五个元素image_data,索引号是4,类型是blob, 并进行绑定。
读取完上述的数据后,就把上述的数据插到map。
3.6rockx_vi_handle_thread线程的讲解
本章节主要介绍rockx_vi_handle_thread线程的工作流程,这个线程最主要的功能是获取第一个VI模块的视频数据,并且使用rockx的人脸检测模块、人脸识别模块、人脸关键点模块对视频流数据进行人脸识别,最后把识别的结果(包括人脸检测区域、人脸识别名称)保存到全局变量里面。
- 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里面通过人脸特征值获取名字,否则就不处理。
- 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设置到全局变量。
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的值。

3.7show_vi_thread线程讲解
本章节主要介绍show_vi_thread线程的工作流程,这个线程最主要的功能是获取第二个VI模块的视频数据,同时获取AI推理线程(rockx_vi_handle_thread)处理的人脸名称和人脸区域信息(rockx_object_array_t)。并通过Opencv把人脸区域和人脸名称显示出来,然后把VI数据发送到VENC编码器。
在第二个流中获取第一个流中的信息,在第二个流中画框和显示名字。发送到编码器中。
二.show_vi_thread线程大体框图

上图是show_vi_thread线程的工作流程,首先要获取第二个VI模块的视频数据,然后获取人脸识别的名称(string )和人脸区域信息(rockx_object_array_t)、利用OPENCV描绘人脸区域的矩形框、利用OPENCV显示人脸名称、把处理后的VI数据发送到VENC编码器。
三.show_vi_thread线程代码截图
3.1. 获取第二个模块VI模块的视频数据

上图是通过RK_MPI_SYS_GetMediaBuffer获取第二个模块的VI数据,模块号是RK_ID_VI,通道号是1。
3.2. 创建OPENCV并获取人脸名称和区域信息

上图是通过OPENCV处理RKMEDIA获取的VI数据,这里的处理也是通过Mat矩阵(Mat rv1126_mat = Mat(HEIGHT, WIDTH, CV_8UC1, RK_MPI_MB_GetPtr(mb)))。然后获取rokx处理线程的区域信息(get_rockx_face_array)和人脸名称(get_rockx_people)。
3.3. 利用OPENCV显示人脸区域信息和人脸名称

这部分的代码是利用OPENCV显示人脸区域和名称,这里要用for循环去查找每个人脸的区域信息,每个人脸的区域信息都是一个box。box里面都存储了四个坐标参数(left、top、right、bottom), 然后计算出x,y(具体的计算在上图)。计算出x,y后,用opencv的rectangle去绘制人脸区域的矩形,这个矩形的width和height都固定成500。
绘制出人脸矩形后,则使用OPENCV的putText函数把get_rockx_people人脸的名称显示到VI模块里面。
3.4. 把处理后的VI数据发送到VENC编码器
![]()
把处理后的VI数据通过RK_MPI_SYS_SendMediaBuffer的API发送到VENC编码器,此时此刻VENC编码器就有H264的编码数据了。
3.8camera_venc_thread线程
本章节主要介绍camera_venc_thread线程的工作流程,这个线程最主要的功能是获取VENC的视频编码码流数据,并把VENC的码流存放到队列里面。
二.camera_venc_thread线程大体框图

上图是camera_venc_thread线程的大体流程图,总共有三步:先获取RK_MPI_SYS_GetMediaBuffer的API获取VENC的码流数据,然后把VENC码流赋值到video_data_packet_t结构体、最后把video_data_packet_t结构体放到队列里面。
三.camera_venc_thread线程代码截图
3.1. 获取VENC码流的视频数据

上图是通过RK_MPI_SYS_GetMediaBuffer获取VENC码流数据,VENC编码通道数是从外面传进来(默认是0),模块号是RK_ID_VENC。
3.2. 把VENC码流数据赋值到video_data_packet_t结构体并入队

分配video_data_packet_t结构体指针,然后把每一帧的VENC码流数据拷贝到video_data_packet_t结构体里面。视频缓冲区的赋值memcpy(video_data_packet->buffer, RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb));,视频长度的赋值是video_data_packet->video_frame_size = RK_MPI_MB_GetSize(mb)。
赋值完上述数据后则把video_data_packet_t的数据放到队列里面,入队的操作是high_video_queue->putVideoPacketQueue(video_data_packet);。
3.9high_video_push_thread线程讲解
本章节主要介绍video_push_thread线程的工作流程,这个线程最主要的功能是获取VIDEO_QUEUE队列的视频数据并用FFMPEG把编码数据发送到流媒体服务器,主要是RTMP流媒体服务器。
二.video_push_thread线程:

上图是video_push_thread线程的大体框图,首先要获取视频队列的数据getVideoPacketQueue、第二步把视频队列的数据赋值到AVPacket、对AVPacket的数据进行PTS时间戳进行累加并转换、利用FFMPEG把AVPacket数据包传输到RTMP流媒体服务器。
三. video_push_thread线程代码截图:
3.1. 从视频队列获取数据并赋值到AVPacket

上图是从视频编码队列获取视频数据并赋值到AVPacket的过程,先调用getVideoPacketQueue从队列获取视频数据,然后把编码的视频数据赋值到AVPacket。具体的步骤是:用av_buffer_realloc分配每一个缓冲区数据,要注意的是AVPacket中缓冲区的buf是不能直接赋值的,比方说: memcpy(pkt->data, video_data_packet->buffer, video_data_packet->frame_size)否则程序就会出现core_dump情况。我们需要先把video_data_packet_t的视频数据(video_data_packet->buffer)先拷贝到pkt->buf->data,然后再把pkt->buf->data的数据赋值到pkt->data。(这里已经省略了FFMPEG的初始化代码)
然后把video_data_packet的video_frame_size长度直接赋值给AVPacket的pkt->size。最后需要添加标识符pkt->flags |= AV_PKT_FLAG_KEY;,添加了这个标识符后,每个AVPacket中都进行关键帧设置,这个标识符必须要加,否则播放器则无法正常解码出视频
3.2. 对AVPacket进行pts的时间戳的累加并转换

根据AVPacket的数据去计算视频的PTS,若AVPacket的数据不为空。则让视频video_packet->pts = ost->next_timestamp++,把视频PTS进行时间基的转换,调用av_packet_rescale_ts把采集的视频时间基转换成复合流的时间基。
3.3. 把每一帧视频数据传输到流媒体服务器

时间基转换完成之后,就把视频数据写入到复合流文件里面,调用的API是av_interleaved_write_frame (注意:复合流文件可以是本地文件也可以是流媒体地址)。
源码:
ffmpeg_audio_queue.cpp
#include "ffmpeg_audio_queue.h"
//AUDIO队列的构造器,包含mutex的初始化和条件变量初始化
AUDIO_QUEUE::AUDIO_QUEUE()
{
pthread_mutex_init(&audioMutex, NULL);//mutex的初始化
pthread_cond_init(&audioCond, NULL);//条件变量初始化
}
//AUDIO队列的析构函数,锁的销毁和条件变量的销毁
AUDIO_QUEUE ::~AUDIO_QUEUE()
{
pthread_mutex_destroy(&audioMutex);
pthread_cond_destroy(&audioCond);
}
//AUDIO_QUEUE的插入音频队列操作
int AUDIO_QUEUE::putAudioPacketQueue(audio_data_packet *audio_packet)
{
pthread_mutex_lock(&audioMutex);//上音频锁
audio_packet_queue.push(audio_packet);//向音频队列插入audio_data_packet包
pthread_cond_broadcast(&audioCond);//唤醒视音频队列
pthread_mutex_unlock(&audioMutex);//解音频锁
return 0;
}
//AUDIO_QUEUE取出音频包
audio_data_packet *AUDIO_QUEUE::getAudioPacketQueue()
{
pthread_mutex_lock(&audioMutex);//上音频锁
while (audio_packet_queue.size() == 0)
{
pthread_cond_wait(&audioCond, &audioMutex);//当音频队列没有数据的时候,等待被唤醒
}
audio_data_packet *item = audio_packet_queue.front();//把音频数据包移到最前面
audio_packet_queue.pop();//pop取出音频数据并删除
pthread_mutex_unlock(&audioMutex);//解音频锁
return item;
}
//AUDIO_QUEUE音频队列长度
int AUDIO_QUEUE::getAudioPacketQueueSize()
{
unsigned int count = 0;
pthread_mutex_lock(&audioMutex);//上音频锁
count = audio_packet_queue.size();//获取音频队列长度
pthread_mutex_unlock(&audioMutex);//解音频锁
return count;
}
ffmpeg_video_queue.cpp
#include "ffmpeg_video_queue.h"
//VIDEO队列的构造器,包含mutex的初始化和条件变量初始化
VIDEO_QUEUE::VIDEO_QUEUE()
{
pthread_mutex_init(&videoMutex, NULL);//mutex的初始化
pthread_cond_init(&videoCond, NULL);//条件变量初始化
}
//VIDEO队列的析构函数,锁的销毁和条件变量的销毁
VIDEO_QUEUE ::~VIDEO_QUEUE()
{
pthread_mutex_destroy(&videoMutex);//锁的销毁
pthread_cond_destroy(&videoCond);//条件变量的销毁
}
//VIDEO_QUEUE的插入视频队列操作
int VIDEO_QUEUE::putVideoPacketQueue(video_data_packet_t *video_packet)
{
pthread_mutex_lock(&videoMutex); //上视频锁
video_packet_queue.push(video_packet);//向视频队列插入video_data_packet_t包
pthread_cond_broadcast(&videoCond);//唤醒视频队列
pthread_mutex_unlock(&videoMutex);//解视频锁
return 0;
}
//VIDEO_QUEUE取出视频包
video_data_packet_t *VIDEO_QUEUE::getVideoPacketQueue()
{
pthread_mutex_lock(&videoMutex);//上视频锁
while (video_packet_queue.size() == 0)
{
pthread_cond_wait(&videoCond, &videoMutex); //当视频队列没有数据的时候,等待被唤醒
}
video_data_packet_t *item = video_packet_queue.front();//把视频数据包移到最前面
video_packet_queue.pop();//pop取出视频数据并删除
pthread_mutex_unlock(&videoMutex);//解视频锁
return item;
}
//VIDEO_QUEUE视频队列长度
int VIDEO_QUEUE::getVideoQueueSize()
{
unsigned int count = 0;
pthread_mutex_lock(&videoMutex);//上视频锁
count = video_packet_queue.size();//获取视频队列长度
pthread_mutex_unlock(&videoMutex);//解视频锁
return count;
}
map_manage.cpp
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include "map_manage.h"
S_THREAD_MAP g_thread_maps[MAX_MAP_NUM];//类对象数组
pthread_mutex_t g_thread_maps_mutex;//定义互斥变量
#if 1
int init_map_manage_function()
{
pthread_mutex_init(&g_thread_maps_mutex, NULL);
memset(g_thread_maps, 0, sizeof(g_thread_maps));
return 0;
}
int set_map_id(unsigned int map_id, unsigned int id)
{
pthread_mutex_lock(&g_thread_maps_mutex);
g_thread_maps[id].map_id = map_id;
pthread_mutex_unlock(&g_thread_maps_mutex);
return 0;
}
int set_thread_map(unsigned int map_id, S_THREAD_MAP *map)
{
pthread_mutex_lock(&g_thread_maps_mutex);
g_thread_maps[map_id] = *map;
pthread_mutex_unlock(&g_thread_maps_mutex);
return 0;
}
unsigned int get_thread_map(unsigned int map_id, S_THREAD_MAP *map)
{
//打开互斥锁
pthread_mutex_lock(&g_thread_maps_mutex);
//对map进行赋值操作
*map = g_thread_maps[map_id];
//关闭互斥锁
pthread_mutex_unlock(&g_thread_maps_mutex);
return 0;
}
#endif
rkmedia_assignment_manage.cpp
#include "rkmedia_assignment_manage.h"
#include "rkmedia_data_process.h"
#include "rkmedia_ffmpeg_config.h"
#include "rkmedia_module.h"
#include "rkmedia_ffmpeg_config.h"
#include "rkmedia_container.h"
int init_rv1126_first_assignment(int protocol_type, char * network_address)
{
int ret;
RKMEDIA_FFMPEG_CONFIG *ffmpeg_config = (RKMEDIA_FFMPEG_CONFIG *)malloc(sizeof(RKMEDIA_FFMPEG_CONFIG));
if (ffmpeg_config == NULL)
{
printf("malloc ffmpeg_config failed\n");
}
ffmpeg_config->width = 1920;
ffmpeg_config->height = 1080;
ffmpeg_config->config_id = 0;
ffmpeg_config->protocol_type = protocol_type;
ffmpeg_config->video_codec = AV_CODEC_ID_H264;
ffmpeg_config->audio_codec = AV_CODEC_ID_AAC;
memcpy(ffmpeg_config->network_addr, network_address, strlen(network_address));
//初始化ffmpeg输出模块
init_rkmedia_ffmpeg_context(ffmpeg_config);
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 * show_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 = 1;
ret = pthread_create(&pid, NULL, show_vi_thread, (void *)show_vi_arg_params);
if (ret != 0)
{
printf("create camera_venc_thread failed\n");
}
//VENC线程的参数
VENC_PROC_PARAM *venc_arg_params = (VENC_PROC_PARAM *)malloc(sizeof(VENC_PROC_PARAM));
if (venc_arg_params == NULL)
{
printf("malloc venc arg error\n");
free(venc_arg_params);
}
venc_arg_params->vencId = 0;
//创建VENC线程,获取摄像头编码数据
ret = pthread_create(&pid, NULL, camera_venc_thread, (void *)venc_arg_params);
if (ret != 0)
{
printf("create camera_venc_thread failed\n");
}
//创建VIDEO_PUSH线程
ret = pthread_create(&pid, NULL, video_push_thread, (void *)ffmpeg_config);
if (ret != 0)
{
printf("push_server_thread error\n");
}
return 0;
}
rkmedia_container.cpp
#include <pthread.h>
#include <string.h>
#include "rkmedia_container.h"
RV1126_ALL_CONTAINER all_containers;
pthread_mutex_t all_containers_mutex;
int init_all_container_function()
{
pthread_mutex_init(&all_containers_mutex, NULL);
memset(&all_containers, 0, sizeof(all_containers));
return 0;
}
int set_vi_container(unsigned int index, RV1126_VI_CONTAINTER *vi_container)
{
pthread_mutex_lock(&all_containers_mutex);
all_containers.vi_containers[index] = *vi_container;
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
int get_vi_container(unsigned int index, RV1126_VI_CONTAINTER *vi_container)
{
pthread_mutex_lock(&all_containers_mutex);
*vi_container = all_containers.vi_containers[index];
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
int set_ai_container(unsigned int index, RV1126_AI_CONTAINTER *ai_container)
{
pthread_mutex_lock(&all_containers_mutex);
all_containers.ai_containers[index] = *ai_container;
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
int get_ai_container(unsigned int index, RV1126_AI_CONTAINTER *ai_container)
{
pthread_mutex_lock(&all_containers_mutex);
*ai_container = all_containers.ai_containers[index];
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
int set_venc_container(unsigned int index, RV1126_VENC_CONTAINER *venc_container)
{
pthread_mutex_lock(&all_containers_mutex);
all_containers.venc_containers[index] = *venc_container;
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
int get_venc_container(unsigned int index, RV1126_VENC_CONTAINER *venc_container)
{
pthread_mutex_lock(&all_containers_mutex);
*venc_container = all_containers.venc_containers[index];
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
int set_aenc_container(unsigned int index, RV1126_AENC_CONTAINER *aenc_container)
{
pthread_mutex_lock(&all_containers_mutex);
all_containers.aenc_containers[index] = *aenc_container;
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
int get_aenc_container(unsigned int index, RV1126_AENC_CONTAINER *aenc_container)
{
pthread_mutex_lock(&all_containers_mutex);
*aenc_container = all_containers.aenc_containers[index];
pthread_mutex_unlock(&all_containers_mutex);
return 0;
}
rkmedia_data_process.cpp
// #include "opencv_queue.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgcodecs/legacy/constants_c.h>
#include "rkmedia_data_process.h"
#include "ffmpeg_video_queue.h"
#include "ffmpeg_audio_queue.h"
#include "rkmedia_module.h"
#include "rkmedia_ffmpeg_config.h"
#include "rkmedia_data_process.h"
#include "map_manage.h"
#include "rockx_data_manage.h"
#include "rockx.h"
#include "cJSON/cJSON.h"
using namespace cv;
extern VIDEO_QUEUE *high_video_queue;
//extern VIDEO_QUEUE *low_video_queue;
int can_read_pic = 0;
int is_upload_to_mqtt_flag = 0;
People mqtt_people;
void nv12ToBgr(uint8_t *data, int width, int height, uint8_t *bgr)
{
cv::Mat yuv(height + height / 2, width, CV_8UC1, (uchar *)data);
cv::Mat bgrMat(height, width, CV_8UC3, bgr);
cv::cvtColor(yuv, bgrMat, cv::COLOR_YUV2BGR_NV12);
}
// 从RV1126视频编码数据赋值到FFMPEG的Video AVPacket中
AVPacket *get_ffmpeg_video_avpacket(AVPacket *pkt)
{
video_data_packet_t *video_data_packet = high_video_queue->getVideoPacketQueue(); // 从视频队列获取数据
if (video_data_packet != NULL)
{
/*
重新分配给定的缓冲区
1. 如果入参的 AVBufferRef 为空,直接调用 av_realloc 分配一个新的缓存区,并调用 av_buffer_create 返回一个新的 AVBufferRef 结构;
2. 如果入参的缓存区长度和入参 size 相等,直接返回 0;
3. 如果对应的 AVBuffer 设置了 BUFFER_FLAG_REALLOCATABLE 标志,或者不可写,再或者 AVBufferRef data 字段指向的数据地址和 AVBuffer 的 data 地址不同,递归调用 av_buffer_realloc 分配一个新
的 buffer,并将 data 拷贝过去;
4. 不满足上面的条件,直接调用 av_realloc 重新分配缓存区。
*/
int ret = av_buffer_realloc(&pkt->buf, video_data_packet->video_frame_size + 70);
if (ret < 0)
{
return NULL;
}
pkt->size = video_data_packet->video_frame_size; // rv1126的视频长度赋值到AVPacket Size
memcpy(pkt->buf->data, video_data_packet->buffer, video_data_packet->video_frame_size); // rv1126的视频数据赋值到AVPacket data
pkt->data = pkt->buf->data; // 把pkt->buf->data赋值到pkt->data
pkt->flags |= AV_PKT_FLAG_KEY; // 默认flags是AV_PKT_FLAG_KEY
if (video_data_packet != NULL)
{
free(video_data_packet);
video_data_packet = NULL;
}
return pkt;
}
else
{
return NULL;
}
}
#if 0
AVPacket *get_low_ffmpeg_video_avpacket(AVPacket *pkt)
{
video_data_packet_t *video_data_packet = low_video_queue->getVideoPacketQueue(); // 从视频队列获取数据
if (video_data_packet != NULL)
{
/*
重新分配给定的缓冲区
1. 如果入参的 AVBufferRef 为空,直接调用 av_realloc 分配一个新的缓存区,并调用 av_buffer_create 返回一个新的 AVBufferRef 结构;
2. 如果入参的缓存区长度和入参 size 相等,直接返回 0;
3. 如果对应的 AVBuffer 设置了 BUFFER_FLAG_REALLOCATABLE 标志,或者不可写,再或者 AVBufferRef data 字段指向的数据地址和 AVBuffer 的 data 地址不同,递归调用 av_buffer_realloc 分配一个新
的 buffer,并将 data 拷贝过去;
4. 不满足上面的条件,直接调用 av_realloc 重新分配缓存区。
*/
int ret = av_buffer_realloc(&pkt->buf, video_data_packet->video_frame_size + 70);
if (ret < 0)
{
return NULL;
}
pkt->size = video_data_packet->video_frame_size; // rv1126的视频长度赋值到AVPacket Size
memcpy(pkt->buf->data, video_data_packet->buffer, video_data_packet->video_frame_size); // rv1126的视频数据赋值到AVPacket data
pkt->data = pkt->buf->data; // 把pkt->buf->data赋值到pkt->data
pkt->flags |= AV_PKT_FLAG_KEY; // 默认flags是AV_PKT_FLAG_KEY
if (video_data_packet != NULL)
{
free(video_data_packet);
video_data_packet = NULL;
}
return pkt;
}
else
{
return NULL;
}
}
#endif
int write_ffmpeg_avpacket(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{
/*将输出数据包时间戳值从编解码器重新调整为流时基 */
av_packet_rescale_ts(pkt, *time_base, st->time_base);
pkt->stream_index = st->index;
return av_interleaved_write_frame(fmt_ctx, pkt);
}
int deal_video_avpacket(AVFormatContext *oc, OutputStream *ost)
{
int ret;
AVCodecContext *c = ost->enc;
AVPacket *video_packet = get_ffmpeg_video_avpacket(ost->packet); // 从RV1126视频编码数据赋值到FFMPEG的Video AVPacket中
if (video_packet != NULL)
{
video_packet->pts = ost->next_timestamp++; // VIDEO_PTS按照帧率进行累加
}
ret = write_ffmpeg_avpacket(oc, &c->time_base, ost->stream, video_packet); // 向复合流写入视频数据
if (ret != 0)
{
printf("write video avpacket error");
return -1;
}
return 0;
}
/*int deal_low_video_avpacket(AVFormatContext *oc, OutputStream *ost)
{
int ret;
AVCodecContext *c = ost->enc;
AVPacket *video_packet = get_low_ffmpeg_video_avpacket(ost->packet); // 从RV1126视频编码数据赋值到FFMPEG的Video AVPacket中
if (video_packet != NULL)
{
video_packet->pts = ost->next_timestamp++; // VIDEO_PTS按照帧率进行累加
}
ret = write_ffmpeg_avpacket(oc, &c->time_base, ost->stream, video_packet); // 向复合流写入视频数据
if (ret != 0)
{
printf("write video avpacket error");
return -1;
}
return 0;
}*/
int nv12_border(char *pic, int pic_w, int pic_h, int rect_x, int rect_y,
int rect_w, int rect_h, int R, int G, int B)
{
/* Set up the rectangle border size */
const int border = 5;
/* RGB convert YUV */
int Y, U, V;
Y = 0.299 * R + 0.587 * G + 0.114 * B;
U = -0.1687 * R + 0.3313 * G + 0.5 * B + 128;
V = 0.5 * R - 0.4187 * G - 0.0813 * B + 128;
/* Locking the scope of rectangle border range */
int j, k;
for (j = rect_y; j < rect_y + rect_h; j++)
{
for (k = rect_x; k < rect_x + rect_w; k++)
{
if (k < (rect_x + border) || k > (rect_x + rect_w - border) ||
j < (rect_y + border) || j > (rect_y + rect_h - border))
{
/* Components of YUV's storage address index */
int y_index = j * pic_w + k;
int u_index =
(y_index / 2 - pic_w / 2 * ((j + 1) / 2)) * 2 + pic_w * pic_h;
int v_index = u_index + 1;
/* set up YUV's conponents value of rectangle border */
pic[y_index] = Y;
pic[u_index] = U;
pic[v_index] = V;
}
}
}
return 0;
}
void *rockx_vi_handle_thread(void *args)
{
pthread_detach(pthread_self());
S_THREAD_MAP thread_map;
get_thread_map(0, &thread_map);
map<People, rockx_face_feature_t> database_face_map = thread_map.thread_people_map;
map<People, rockx_face_feature_t>::iterator database_iter;
MEDIA_BUFFER src_mb = NULL;
rockx_module_t data_version;
data_version = ROCKX_MODULE_FACE_DETECTION_V2;
rockx_ret_t rockx_ret;
rockx_handle_t face_det_handle;
rockx_handle_t face_recognize_handle;
rockx_handle_t face_5landmarks_handle;
rockx_handle_t face_masks_det_handle;
rockx_config_t *config = rockx_create_config();
rockx_add_config(config, ROCKX_CONFIG_DATA_PATH, "/userdata/rockx_data/");
rockx_ret = rockx_create(&face_det_handle, data_version, config, sizeof(rockx_config_t));
if (rockx_ret != ROCKX_RET_SUCCESS)
{
printf("init face_detect error %d\n", rockx_ret);
return NULL;
}
rockx_ret = rockx_create(&face_recognize_handle, ROCKX_MODULE_FACE_RECOGNIZE, config, sizeof(rockx_config_t));
if (rockx_ret != ROCKX_RET_SUCCESS)
{
printf("init face_recognize error %d\n", rockx_ret);
return NULL;
}
rockx_ret = rockx_create(&face_5landmarks_handle, ROCKX_MODULE_FACE_LANDMARK_5, config, 0);
if (rockx_ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n",
rockx_ret);
}
/*rockx_ret = rockx_create(&face_masks_det_handle,
ROCKX_MODULE_FACE_MASKS_DETECTION, config, 0);
if (rockx_ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n",
rockx_ret);
}*/
rockx_image_t input_image;
input_image.width = 1920;
input_image.height = 1080;
input_image.pixel_format = ROCKX_PIXEL_FORMAT_YUV420SP_NV12;
int ret;
bool is_recognize = false;
string predict;
int face_count = 0;
uint8_t *bgr = new uint8_t[1920 * 1080 * 3];
int count = 0;
People people;
while (1)
{
src_mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 0, -1);
if (!src_mb)
{
printf("");
break;
}
input_image.size = RK_MPI_MB_GetSize(src_mb);
input_image.data = (unsigned char *)RK_MPI_MB_GetPtr(src_mb); // 从指定的MEDIA_BUFFER中获取缓冲区数据指针
rockx_object_array_t face_array;
memset(&face_array, 0, sizeof(face_array));
// 开始人脸检测
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);
// printf("face_count = %d\n", face_array.count);
if (face_array.count > 0)
{
for (int i = 0; i < face_array.count; i++)
{
if (1)
{
int is_false_face;
// 进行人脸过滤处理
ret = rockx_face_filter(face_5landmarks_handle, &input_image,
&face_array.object[i].box, &is_false_face);
if (ret != ROCKX_RET_SUCCESS)
{
printf("rockx_face_filter error %d\n", ret);
}
if (is_false_face)
continue;
}
#if 1
// 人脸检测结果(包括人脸、车牌、头部、物体等)变量定义
rockx_object_t max_face;
rockx_object_t cur_face = face_array.object[i];
// 进行人脸区域计算处理操作
int cur_face_box_area = (cur_face.box.right - cur_face.box.left) *
(cur_face.box.bottom - cur_face.box.top);
int max_face_box_area = (max_face.box.right - max_face.box.left) *
(max_face.box.bottom - max_face.box.top);
if (cur_face_box_area > max_face_box_area)
{
max_face = cur_face;
}
// 检测输出处理
rockx_image_t out_img;
memset(&out_img, 0, sizeof(rockx_image_t));
// 进行面部矫正对齐
ret = rockx_face_align(face_5landmarks_handle, &input_image, &(max_face.box), NULL, &out_img);
if (ret != ROCKX_RET_SUCCESS)
{
printf("face_align failed\n");
}
rockx_face_feature_t rv1126_feature;
rockx_face_recognize(face_recognize_handle, &out_img, &rv1126_feature);
for (database_iter = database_face_map.begin();
database_iter != database_face_map.end(); database_iter++)
{
float similarity;
ret = rockx_face_feature_similarity(&database_iter->second,
&rv1126_feature, &similarity);
printf("simple_value = %lf\n", similarity);
if (similarity <= 1.0)
{
is_recognize = true;
break;
}
else
{
is_recognize = false;
continue;
}
}
if (is_recognize == true)
{
people = database_iter->first;
printf("people_name = %s\n", people.people_name.c_str());
}
set_rockx_people(people);
#endif
}
}
RK_MPI_MB_ReleaseBuffer(src_mb);
src_mb = NULL;
}
}
void *show_vi_thread(void *args)
{
pthread_detach(pthread_self());
MEDIA_BUFFER mb = NULL;
float x_rate = (float)1920 / 1920;
float y_rate = (float)1920 / 1080;
Point pointer;
pointer.x = 300;
pointer.y = 300;
while (1)
{
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 1, -1);
if (!mb)
{
printf("RK_MPI_SYS_GetMediaBuffer Break.....\n");
break;
}
Mat rv1126_mat = Mat(HEIGHT, WIDTH, CV_8UC1, RK_MPI_MB_GetPtr(mb));
rockx_object_array_t face_array = get_rockx_face_array();
People people = get_rockx_people();
// string face_count_str = std::to_string(face_array.count);
// cv::putText(rv1126_mat, face_count_str, pointer, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 255), 3);
for (int i = 0; i < face_array.count; i++)
{
int x = face_array.object[i].box.left * x_rate;
int y = face_array.object[i].box.top * y_rate;
int w = (face_array.object[i].box.right - face_array.object[i].box.left) * x_rate;
int h = (face_array.object[i].box.bottom - face_array.object[i].box.top) * y_rate;
if (x < 0)
x = 0;
if (y < 0)
y = 0;
while ((uint32_t)(x + w) >= 1920)
{
w -= 16;
}
while ((uint32_t)(y + h) >= 1080)
{
h -= 16;
}
Scalar color(255, 0, 255);
Rect boundingBox(x, y, 500, 500);
int thickness = 3;
rectangle(rv1126_mat, boundingBox, color, thickness);
int baseline;
Size text_size = getTextSize(people.people_name, 2, 2, 2, &baseline);
Point origin;
origin.x = rv1126_mat.cols / 4 - text_size.width / 4;
origin.y = rv1126_mat.rows / 4 + text_size.height / 4;
cv::putText(rv1126_mat, people.people_name, origin, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 255), 3);
}
RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, 0, mb);
RK_MPI_MB_ReleaseBuffer(mb);
}
return NULL;
}
void *camera_venc_thread(void *args)
{
pthread_detach(pthread_self());
MEDIA_BUFFER mb = NULL;
VENC_PROC_PARAM venc_arg = *(VENC_PROC_PARAM *)args;
free(args);
printf("video_venc_thread...\n");
while (1)
{
// 从指定通道中获取VENC数据
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, venc_arg.vencId, -1);
if (!mb)
{
printf("high_get venc media buffer error\n");
break;
}
// int naluType = RK_MPI_MB_GetFlag(mb);
// 分配video_data_packet_t结构体
video_data_packet_t *video_data_packet = (video_data_packet_t *)malloc(sizeof(video_data_packet_t));
// 把VENC视频缓冲区数据传输到video_data_packet的buffer中
memcpy(video_data_packet->buffer, RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb));
// 把VENC的长度赋值给video_data_packet的video_frame_size中
video_data_packet->video_frame_size = RK_MPI_MB_GetSize(mb);
// video_data_packet->frame_flag = naluType;
// 入到视频压缩队列
high_video_queue->putVideoPacketQueue(video_data_packet);
// printf("#naluType = %d \n", naluType);
// 释放VENC资源
RK_MPI_MB_ReleaseBuffer(mb);
}
MPP_CHN_S vi_channel;
MPP_CHN_S venc_channel;
vi_channel.enModId = RK_ID_VI;
vi_channel.s32ChnId = 0;
venc_channel.enModId = RK_ID_VENC;
venc_channel.s32ChnId = venc_arg.vencId;
int ret;
ret = RK_MPI_SYS_UnBind(&vi_channel, &venc_channel);
if (ret != 0)
{
printf("VI UnBind failed \n");
}
else
{
printf("Vi UnBind success\n");
}
ret = RK_MPI_VENC_DestroyChn(0);
if (ret)
{
printf("Destroy Venc error! ret=%d\n", ret);
return 0;
}
// destroy vi
ret = RK_MPI_VI_DisableChn(0, 0);
if (ret)
{
printf("Disable Chn Venc error! ret=%d\n", ret);
return 0;
}
return NULL;
}
void *get_rga_thread(void *args)
{
MEDIA_BUFFER mb = NULL;
while (1)
{
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA, 0, -1); // 获取RGA的数据
if (!mb)
{
break;
}
RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, 1, mb); //
RK_MPI_MB_ReleaseBuffer(mb);
}
return NULL;
}
#if 0
void *low_camera_venc_thread(void *args)
{
pthread_detach(pthread_self());
MEDIA_BUFFER mb = NULL;
VENC_PROC_PARAM venc_arg = *(VENC_PROC_PARAM *)args;
free(args);
printf("low_video_venc_thread...\n");
while (1)
{
// 从指定通道中获取VENC数据
// mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, venc_arg.vencId, -1);
mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, 1, -1);
if (!mb)
{
printf("low_venc break....\n");
break;
}
// int naluType = RK_MPI_MB_GetFlag(mb);
// 分配video_data_packet_t结构体
video_data_packet_t *video_data_packet = (video_data_packet_t *)malloc(sizeof(video_data_packet_t));
// 把VENC视频缓冲区数据传输到video_data_packet的buffer中
memcpy(video_data_packet->buffer, RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb));
// 把VENC的长度赋值给video_data_packet的video_frame_size中
video_data_packet->video_frame_size = RK_MPI_MB_GetSize(mb);
// video_data_packet->frame_flag = naluType;
// 入到视频压缩队列
low_video_queue->putVideoPacketQueue(video_data_packet);
// printf("#naluType = %d \n", naluType);
// 释放VENC资源
RK_MPI_MB_ReleaseBuffer(mb);
}
return NULL;
}
#endif
// 音视频合成推流线程
void *video_push_thread(void *args)
{
pthread_detach(pthread_self());
RKMEDIA_FFMPEG_CONFIG ffmpeg_config = *(RKMEDIA_FFMPEG_CONFIG *)args;
free(args);
AVOutputFormat *fmt = NULL;
int ret;
while (1)
{
ret = deal_video_avpacket(ffmpeg_config.oc, &ffmpeg_config.video_stream); // 处理FFMPEG视频数据
if (ret == -1)
{
printf("deal_video_avpacket error\n");
break;
}
}
av_write_trailer(ffmpeg_config.oc); // 写入AVFormatContext的尾巴
free_stream(ffmpeg_config.oc, &ffmpeg_config.video_stream); // 释放VIDEO_STREAM的资源
free_stream(ffmpeg_config.oc, &ffmpeg_config.audio_stream); // 释放AUDIO_STREAM的资源
avio_closep(&ffmpeg_config.oc->pb); // 释放AVIO资源
avformat_free_context(ffmpeg_config.oc); // 释放AVFormatContext资源
return NULL;
}
#if 0
void *low_video_push_thread(void *args)
{
pthread_detach(pthread_self());
RKMEDIA_FFMPEG_CONFIG ffmpeg_config = *(RKMEDIA_FFMPEG_CONFIG *)args;
free(args);
AVOutputFormat *fmt = NULL;
int ret;
while (1)
{
ret = deal_low_video_avpacket(ffmpeg_config.oc, &ffmpeg_config.video_stream); // 处理FFMPEG视频数据
if (ret == -1)
{
printf("deal_video_avpacket error\n");
break;
}
}
av_write_trailer(ffmpeg_config.oc); // 写入AVFormatContext的尾巴
free_stream(ffmpeg_config.oc, &ffmpeg_config.video_stream); // 释放VIDEO_STREAM的资源
free_stream(ffmpeg_config.oc, &ffmpeg_config.audio_stream); // 释放AUDIO_STREAM的资源
avio_closep(&ffmpeg_config.oc->pb); // 释放AVIO资源
avformat_free_context(ffmpeg_config.oc); // 释放AVFormatContext资源
return NULL;
}
#endif
void *mqtt_cjson_upload_thread(void *args)
{
int mqtt_flag = 0;
while (1)
{
if (is_upload_to_mqtt_flag == 1)
{
mqtt_flag = 1;
}
if (mqtt_flag == 1 && is_upload_to_mqtt_flag == 1)
{
cJSON *json = cJSON_CreateObject();
cJSON_AddStringToObject(json, "name", mqtt_people.people_name.c_str());
cJSON_AddStringToObject(json, "image_data", mqtt_people.images.data());
// cJSON_AddStringToObject(json, "image_length", people.images.data());
char *str_print = cJSON_Print(json);
printf("JSON_Str = %s\n", str_print);
mqtt_flag = 0;
}
else
{
printf("No Upload....\n");
}
}
}
rkmedia_ffmpeg_config.cpp
#include "rkmedia_ffmpeg_config.h"
RKMEDIA_FFMPEG_CONFIG rkmedia_ffmpeg_configs[NETWORK_NUM];
pthread_mutex_t rkmedia_ffmpeg_config_mutex;
int init_rkmedia_ffmpeg_function()
{
pthread_mutex_init(&rkmedia_ffmpeg_config_mutex, NULL);
memset(rkmedia_ffmpeg_configs, 0, sizeof(rkmedia_ffmpeg_configs));
return 0;
}
int set_rkmedia_ffmpeg_config(unsigned int config_id, RKMEDIA_FFMPEG_CONFIG *ffmpeg_config)
{
pthread_mutex_lock(&rkmedia_ffmpeg_config_mutex);
rkmedia_ffmpeg_configs[config_id] = *ffmpeg_config;
pthread_mutex_unlock(&rkmedia_ffmpeg_config_mutex);
return 0;
}
unsigned int get_rkmedia_ffmpeg_config(unsigned int config_id, RKMEDIA_FFMPEG_CONFIG *ffmpeg_config)
{
pthread_mutex_lock(&rkmedia_ffmpeg_config_mutex);
*ffmpeg_config = rkmedia_ffmpeg_configs[config_id];
pthread_mutex_unlock(&rkmedia_ffmpeg_config_mutex);
return 0;
}
int add_stream(OutputStream *ost, AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id, int width, int height)
{
AVCodecContext *c = NULL;
//创建输出码流的AVStream, AVStream是存储每一个视频/音频流信息的结构体
ost->stream = avformat_new_stream(oc, NULL);
if (!ost->stream)
{
printf("Can't not avformat_new_stream\n");
return 0;
}
else
{
printf("Success avformat_new_stream\n");
}
//通过codecid找到CODEC
*codec = avcodec_find_encoder(codec_id);
if (!(*codec))
{
printf("Can't not find any encoder");
return 0;
}
else
{
printf("Success find encoder");
}
//nb_streams 输入视频的AVStream 个数 就是当前有几种Stream,比如视频流、音频流、字幕,这样就算三种了,
// s->nb_streams - 1其实对应的应是AVStream 中的 index
ost->stream->id = oc->nb_streams - 1;
//通过CODEC分配编码器上下文
c = avcodec_alloc_context3(*codec);
if (!c)
{
printf("Can't not allocate context3\n");
return 0;
}
else
{
printf("Success allocate context3");
}
ost->enc = c;
switch ((*codec)->type)
{
case AVMEDIA_TYPE_AUDIO:
c->sample_fmt = (*codec)->sample_fmts ? (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; //FFMPEG采样格式
c->bit_rate = 153600; //FFMPEG音频码率
c->sample_rate = 48000; //FFMPEG采样率
c->channel_layout = AV_CH_LAYOUT_STEREO;//FFMPEG声道数2
c->channels = av_get_channel_layout_nb_channels(c->channel_layout); //FFMPEG采样通道
ost->stream->time_base = (AVRational){1, c->sample_rate};//FFMPEG音频时间基
break;
case AVMEDIA_TYPE_VIDEO:
//c->codec_id = codec_id;
c->bit_rate = width * height * 3; //FFMPEG视频码率
//分辨率必须是2的倍数
c->width = width; //FFMPEG视频宽度
c->height = height;//FFMPEG视频高度
ost->stream->r_frame_rate.den = 1; //FFMPEG帧率,分母
ost->stream->r_frame_rate.num = 25;//FFMPEG帧率,分子
ost->stream->time_base = (AVRational){1, 25};//Stream视频时间基,默认情况下等于帧率
c->time_base = ost->stream->time_base; //编码器时间基
c->gop_size = GOPSIZE; //GOPSIZE
c->pix_fmt = AV_PIX_FMT_NV12;//图像格式
break;
default:
break;
}
//在h264头部添加SPS,PPS
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
{
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
return 0;
}
//使能video编码器
int open_video(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
AVCodecContext *c = ost->enc;
//打开编码器
avcodec_open2(c, codec, NULL);
//分配video avpacket包
ost->packet = av_packet_alloc();
/* 将AVCodecContext参数复制AVCodecParameters复用器 */
avcodec_parameters_from_context(ost->stream->codecpar, c);
return 0;
}
//使能audio编码器
int open_audio(AVFormatContext *oc, AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg)
{
AVCodecContext *c = ost->enc;
//打开编码器
avcodec_open2(c, codec, NULL);
//分配 audio avpacket包
ost->packet = av_packet_alloc();
/* 将AVCodecContext参数复制AVCodecParameters复用器 */
avcodec_parameters_from_context(ost->stream->codecpar, c);
return 0;
}
void free_stream(AVFormatContext *oc, OutputStream *ost)
{
avcodec_close(ost->enc); //就是需要关闭的编码器的AVCodecContext
avcodec_free_context(&ost->enc); //释放AVCodecContext上下文
av_buffer_unref(&(ost->packet->buf));//释放AVBufferRef
av_packet_unref(ost->packet); //释放AVPacket
av_packet_free(&ost->packet); //释放AVPacket
}
int init_rkmedia_ffmpeg_context(RKMEDIA_FFMPEG_CONFIG *ffmpeg_config)
{
AVOutputFormat *fmt = NULL;
AVCodec *audio_codec = NULL;
AVCodec *video_codec = NULL;
int ret = 0;
//FLV_PROTOCOL is RTMP TCP
if (ffmpeg_config->protocol_type == FLV_PROTOCOL)
{
//初始化一个FLV的AVFormatContext
ret = avformat_alloc_output_context2(&ffmpeg_config->oc, NULL, "flv", ffmpeg_config->network_addr);
if (ret < 0)
{
return -1;
}
}
//TS_PROTOCOL is SRT UDP RTSP
else if (ffmpeg_config->protocol_type == TS_PROTOCOL)
{
//初始化一个TS的AVFormatContext
ret = avformat_alloc_output_context2(&ffmpeg_config->oc, NULL, "mpegts", ffmpeg_config->network_addr);
if (ret < 0)
{
return -1;
}
}
fmt = ffmpeg_config->oc->oformat;
/*指定编码器*/
fmt->video_codec = ffmpeg_config->video_codec;
fmt->audio_codec = ffmpeg_config->audio_codec;
if (fmt->video_codec != AV_CODEC_ID_NONE)
{
ret = add_stream(&ffmpeg_config->video_stream, ffmpeg_config->oc, &video_codec, fmt->video_codec,ffmpeg_config->width,ffmpeg_config->height);
if (ret < 0)
{
avcodec_free_context(&ffmpeg_config->video_stream.enc);
free_stream(ffmpeg_config->oc, &ffmpeg_config->video_stream);
avformat_free_context(ffmpeg_config->oc);
return -1;
}
ret = open_video(ffmpeg_config->oc, video_codec, &ffmpeg_config->video_stream, NULL);
if (ret < 0)
{
avformat_free_context(ffmpeg_config->oc);
}
}
#if 0
if (fmt->audio_codec != AV_CODEC_ID_NONE)
{
ret = add_stream(&ffmpeg_config->audio_stream, ffmpeg_config->oc, &audio_codec, fmt->audio_codec);
if (ret < 0)
{
avcodec_free_context(&ffmpeg_config->audio_stream.enc);
free_stream(ffmpeg_config->oc, &ffmpeg_config->audio_stream);
avformat_free_context(ffmpeg_config->oc);
return -1;
}
ret = open_audio(ffmpeg_config->oc, audio_codec, &ffmpeg_config->audio_stream, NULL);
if (ret < 0)
{
avformat_free_context(ffmpeg_config->oc);
}
}
#endif
av_dump_format(ffmpeg_config->oc, 0, ffmpeg_config->network_addr, 1);
if (!(fmt->flags & AVFMT_NOFILE))
{
//打开输出文件
ret = avio_open(&ffmpeg_config->oc->pb, ffmpeg_config->network_addr, AVIO_FLAG_WRITE);
if (ret < 0)
{
free_stream(ffmpeg_config->oc, &ffmpeg_config->video_stream);
free_stream(ffmpeg_config->oc, &ffmpeg_config->audio_stream);
avformat_free_context(ffmpeg_config->oc);
return -1;
}
}
avformat_write_header(ffmpeg_config->oc, NULL);
return 0;
}
rkmedia_module.cpp
#include "rkmedia_module.h"
int rkmedia_function_init()
{
RK_MPI_SYS_Init();
return 0;
}
int rkmedia_vi_init(RV1126_VI_CONFIG *rv1126_vi_config)
{
int ret;
VI_CHN_ATTR_S vi_attr = rv1126_vi_config->attr;
unsigned int id = rv1126_vi_config->id;
//vi_attr.pcVideoNode = CMOS_DEVICE_NAME;//
//初始化VI模块
ret = RK_MPI_VI_SetChnAttr(CAMERA_ID, id, &vi_attr);
//使能VI模块
ret |= RK_MPI_VI_EnableChn(CAMERA_ID, id);
if (ret != 0)
{
printf("create vi failed.....\n", ret);
return -1;
}
return 0;
}
int rkmedia_ai_init(RV1126_AI_CONFIG *rv1126_ai_config)
{
int ret;
AI_CHN_ATTR_S ai_attr = rv1126_ai_config->attr;
unsigned int id = rv1126_ai_config->id;
//ai_attr.pcAudioNode = AUDIO_PATH;
ret = RK_MPI_AI_SetChnAttr(id, &ai_attr); //设置AI属性
ret = RK_MPI_AI_EnableChn(id); //使能AI模块
if (ret != 0)
{
printf("create ai failed...\n");
return -1;
}
return 0;
}
//VENC的初始化
int rkmedia_venc_init(RV1126_VENC_CONFIG *rv1126_venc_config)
{
int ret;
VENC_CHN_ATTR_S venc_chn_attr = rv1126_venc_config->attr;
unsigned int venc_id = rv1126_venc_config->id;
ret = RK_MPI_VENC_CreateChn(rv1126_venc_config->id, &venc_chn_attr);
if (ret != 0)
{
printf("create rv1126_venc_module failed\n");
return -1;
}
else
{
printf("create rv1126_venc_module success\n");
}
return 0;
}
//初始化AENC参数
int rkmedia_aenc_init(RV1126_AENC_CONFIG *rv1126_aenc_config)
{
int ret;
AENC_CHN_ATTR_S aenc_chn_attr = rv1126_aenc_config->attr;
unsigned int aenc_id = rv1126_aenc_config->id;
//创建AENC层
ret = RK_MPI_AENC_CreateChn(aenc_id, &aenc_chn_attr);
if (ret != 0)
{
printf("create aenc failed: %d\n");
return -1;
}
return 0;
}
rkmedia_module_function.cpp
#include "rkmedia_module_function.h"
#include "rkmedia_assignment_manage.h"
#include "rkmedia_config_public.h"
#include "rkmedia_module.h"
#include "rkmedia_container.h"
#include "SDL.h"
#include "SDL_ttf.h"
#include <sys/time.h>
#define FILE_IMAGE_LENGTH (64 * 1024)
static int get_align16_value(int input_value, int align)
{
int handle_value = 0;
if (align && (input_value % align))
handle_value = (input_value / align + 1) * align;
return handle_value;
}
int read_image(char *filename, char *buffer)
{
if (filename == NULL || buffer == NULL)
return -1;
FILE *fp = fopen(filename, "rb"); // 以二进制模式读取该文件
if (fp == NULL)
{
printf("fopen failed\n");
return -2;
}
// 检测文件大小file
fseek(fp, 0, SEEK_END);
int length = ftell(fp);
fseek(fp, 0, SEEK_SET);
int size = fread(buffer, 1, length, fp);
if (size != length)
{
printf("fread failed:%d\n", size);
return -3;
}
fclose(fp);
return size;
}
static int get_cur_time_ms(void)
{
struct timeval tv;
gettimeofday(&tv, NULL); // 使用gettimeofday获取当前系统时间
return (tv.tv_sec * 1000 + tv.tv_usec / 1000); // 利用struct timeval结构体将时间转换为ms
}
int init_rkmedia_module_function()
{
rkmedia_function_init();
RV1126_VI_CONFIG rockx_rkmedia_vi_config;
memset(&rockx_rkmedia_vi_config, 0, sizeof(rockx_rkmedia_vi_config));
rockx_rkmedia_vi_config.id = 0;
rockx_rkmedia_vi_config.attr.pcVideoNode = AI_CMOS_DEVICE_NAME; // VIDEO视频节点路径,
rockx_rkmedia_vi_config.attr.u32BufCnt = 3; // VI捕获视频缓冲区计数,默认是3
rockx_rkmedia_vi_config.attr.u32Width = 1920; // 视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
rockx_rkmedia_vi_config.attr.u32Height = 1080; // 视频输入的高度,一般和CMOS摄像头或者外设的高度一致
rockx_rkmedia_vi_config.attr.enPixFmt = IMAGE_TYPE_NV12; // 视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
rockx_rkmedia_vi_config.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // VI捕捉视频的类型
rockx_rkmedia_vi_config.attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
int ret = rkmedia_vi_init(&rockx_rkmedia_vi_config); // 初始化VI工作
if (ret != 0)
{
printf("rockx_vi init error\n");
}
else
{
printf("rockx_vi init success\n");
RV1126_VI_CONTAINTER vi_container;
vi_container.id = 0;
vi_container.vi_id = rockx_rkmedia_vi_config.id;
set_vi_container(0, &vi_container); // 设置VI容器
}
RV1126_VI_CONFIG show_rkmedia_vi_config;
memset(&show_rkmedia_vi_config, 0, sizeof(show_rkmedia_vi_config));
show_rkmedia_vi_config.id = 1;
show_rkmedia_vi_config.attr.pcVideoNode = SHOW_CMOS_DEVICE_NAME; // VIDEO视频节点路径,
show_rkmedia_vi_config.attr.u32BufCnt = 3; // VI捕获视频缓冲区计数,默认是3
show_rkmedia_vi_config.attr.u32Width = 1920; // 视频输入的宽度,一般和CMOS摄像头或者外设的宽度一致
show_rkmedia_vi_config.attr.u32Height = 1080; // 视频输入的高度,一般和CMOS摄像头或者外设的高度一致
show_rkmedia_vi_config.attr.enPixFmt = IMAGE_TYPE_NV12; // 视频输入的图像格式,默认是NV12(IMAGE_TYPE_NV12)
show_rkmedia_vi_config.attr.enBufType = VI_CHN_BUF_TYPE_MMAP; // VI捕捉视频的类型
show_rkmedia_vi_config.attr.enWorkMode = VI_WORK_MODE_NORMAL; // VI的工作模式,默认是NORMAL(VI_WORK_MODE_NORMAL)
ret = rkmedia_vi_init(&show_rkmedia_vi_config); // 初始化VI工作
if (ret != 0)
{
printf("show_vi init error\n");
}
else
{
printf("show_vi init success\n");
RV1126_VI_CONTAINTER vi_container;
vi_container.id = 1;
vi_container.vi_id = show_rkmedia_vi_config.id;
set_vi_container(1, &vi_container); // 设置VI容器
}
RV1126_VENC_CONFIG rkmedia_venc_config = {0};
memset(&rkmedia_venc_config, 0, sizeof(rkmedia_venc_config));
rkmedia_venc_config.id = 0;
rkmedia_venc_config.attr.stVencAttr.enType = RK_CODEC_TYPE_H264; // 编码器协议类型
rkmedia_venc_config.attr.stVencAttr.imageType = IMAGE_TYPE_NV12; // 输入图像类型
rkmedia_venc_config.attr.stVencAttr.u32PicWidth = 1920; // 编码图像宽度
rkmedia_venc_config.attr.stVencAttr.u32PicHeight = 1080; // 编码图像高度
rkmedia_venc_config.attr.stVencAttr.u32VirWidth = 1920; // 编码图像虚宽度,一般来说u32VirWidth和u32PicWidth是一致的
rkmedia_venc_config.attr.stVencAttr.u32VirHeight = 1080; // 编码图像虚高度,一般来说u32VirHeight和u32PicHeight是一致的
rkmedia_venc_config.attr.stVencAttr.u32Profile = 66; // 编码等级H.264: 66: Baseline; 77:Main Profile; 100:High Profile; H.265: default:Main; Jpege/MJpege: default:Baseline(编码等级的作用主要是改变画面质量,66的画面质量最差利于网络传输,100的质量最好)
rkmedia_venc_config.attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR; // 编码器码率控制模式
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32Gop = 25; // GOPSIZE:关键帧间隔
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32BitRate = 1920 * 1080 * 3; // 码率
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1; // 目的帧率分子:填的是1固定
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25; // 目的帧率分母:填的是25固定
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1; // 源头帧率分子:填的是1固定
rkmedia_venc_config.attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25; // 源头帧率分母:填的是25固定
ret = rkmedia_venc_init(&rkmedia_venc_config); // VENC模块的初始化
if (ret != 0)
{
printf("venc init error\n");
}
else
{
RV1126_VENC_CONTAINER venc_container;
venc_container.id = 0;
venc_container.venc_id = rkmedia_venc_config.id;
set_venc_container(0, &venc_container);
printf("venc init success\n");
}
ret = RK_MPI_VI_StartStream(CAMERA_ID, 0);
if(ret)
{
printf("RK_MPI_VI_StartStream_0 Failed....\n");
}
ret = RK_MPI_VI_StartStream(CAMERA_ID, 1);
if(ret)
{
printf("RK_MPI_VI_StartStream_1 Failed....\n");
}
return 0;
}
rockx_data_manage.cpp
#include "rockx_data_manage.h"
#include <string>
rockx_object_array_t face_array_bak;
string predictName;
rockx_face_result_t rockx_face_result;
rockx_face_result_group_t face_result_group;
People rockx_people;
pthread_mutex_t g_face_array_mutex;
pthread_mutex_t g_face_predict_mutex;
pthread_mutex_t g_face_result_mutex;
pthread_mutex_t g_face_people_mutex;
int init_all_rockx_face_data()
{
pthread_mutex_init(&g_face_array_mutex, NULL);
pthread_mutex_init(&g_face_predict_mutex, NULL);
pthread_mutex_init(&g_face_result_mutex, NULL);
pthread_mutex_init(&g_face_people_mutex, NULL);
}
void set_rockx_face_array(rockx_object_array_t face_array)
{
pthread_mutex_lock(&g_face_array_mutex);
face_array_bak = face_array;
pthread_mutex_unlock(&g_face_array_mutex);
}
rockx_object_array_t get_rockx_face_array()
{
pthread_mutex_lock(&g_face_array_mutex);
rockx_object_array_t face_array = face_array_bak;
pthread_mutex_unlock(&g_face_array_mutex);
return face_array;
}
void set_rockx_prdict_name(string name)
{
pthread_mutex_lock(&g_face_array_mutex);
predictName = name;
pthread_mutex_unlock(&g_face_array_mutex);
}
string get_rockx_prdict_name()
{
pthread_mutex_lock(&g_face_array_mutex);
string predict_name = predictName;
pthread_mutex_unlock(&g_face_array_mutex);
return predict_name;
}
void set_rockx_face_resuslt_group(rockx_face_result_group_t face_result_group_t)
{
pthread_mutex_lock(&g_face_result_mutex);
face_result_group = face_result_group_t;
pthread_mutex_unlock(&g_face_result_mutex);
}
rockx_face_result_group_t get_rockx_face_resuslt_group()
{
pthread_mutex_lock(&g_face_result_mutex);
rockx_face_result_group_t face_result_group_t = face_result_group;
pthread_mutex_unlock(&g_face_result_mutex);
return face_result_group_t;
}
void set_rockx_people(People people)
{
pthread_mutex_lock(&g_face_people_mutex);
rockx_people = people;
pthread_mutex_unlock(&g_face_people_mutex);
}
People get_rockx_people()
{
pthread_mutex_lock(&g_face_people_mutex);
People rockx_people_t = rockx_people;
pthread_mutex_unlock(&g_face_people_mutex);
return rockx_people_t;
}
rv1126_ffmpeg_main.cpp
#include "rkmedia_ffmpeg_config.h"
#include "rkmedia_container.h"
#include "ffmpeg_audio_queue.h"
#include "ffmpeg_video_queue.h"
#include "rkmedia_module_function.h"
#include "rkmedia_assignment_manage.h"
#include "map_manage.h"
#include "sqlite3_operation.h"
#include "rv1126_v4l2_camera.h"
#include "rockx.h"
VIDEO_QUEUE *high_video_queue = NULL;
void init_face_data()
{
Connection_sqlite3DataBase();
int task_id = 0;
S_THREAD_MAP thread_map;
map<People, rockx_face_feature_t> maps = QueryPeopleData();
thread_map.thread_people_map = maps;
set_thread_map(task_id, &thread_map);
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("Please Input ./rv1126_ffmpeg_main high_stream_type high_url_address . Notice URL_TYPE: 0-->FLV 1-->TS\n");
return -1;
}
int high_protocol_type = atoi(argv[1]);
char *high_network_address = argv[2];
init_rkmedia_module_function(); // 初始化所有rkmedia的模块
high_video_queue = new VIDEO_QUEUE(); // 初始化所有VIDEO队列
init_face_data(); //初始化人脸数据
init_rv1126_first_assignment(high_protocol_type, high_network_address); // 开启推流任务
while (1)
{
sleep(20);
}
return 0;
}
rv1126_isp_function.cpp
#include "rv1126_isp_function.h"
int init_all_isp_function()
{
rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
SAMPLE_COMM_ISP_Init(hdr_mode, RK_FALSE);
SAMPLE_COMM_ISP_Run();
SAMPLE_COMM_ISP_SetFrameRate(30);
return 0;
}
sample_common_isp.c
// Copyright 2020 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#if 1
#include "sample_common.h"
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
static rk_aiq_sys_ctx_t *g_aiq_ctx;
RK_S32 SAMPLE_COMM_ISP_Init(rk_aiq_working_mode_t WDRMode, RK_BOOL bFECEnable) {
char *iq_file_dir = "/etc/iqfiles/";
setlinebuf(stdout);
rk_aiq_sys_ctx_t *aiq_ctx;
rk_aiq_static_info_t aiq_static_info;
rk_aiq_uapi_sysctl_enumStaticMetas(0, &aiq_static_info);
printf("sensor_name is %s, iqfiles is %s\n",
aiq_static_info.sensor_info.sensor_name, iq_file_dir);
aiq_ctx = rk_aiq_uapi_sysctl_init(aiq_static_info.sensor_info.sensor_name,
iq_file_dir, NULL, NULL);
printf("rk_aiq_uapi_sysctl_init bFECEnable %d\n", bFECEnable);
rk_aiq_uapi_setFecEn(aiq_ctx, bFECEnable);
printf("rk_aiq_uapi_setFecEn\n");
if (rk_aiq_uapi_sysctl_prepare(aiq_ctx, 0, 0, WDRMode)) {
printf("rkaiq engine prepare failed !\n");
return -1;
}
printf("rk_aiq_uapi_sysctl_init/prepare succeed\n");
g_aiq_ctx = aiq_ctx;
return 0;
}
RK_VOID SAMPLE_COMM_ISP_Stop(void) {
if (!g_aiq_ctx)
return;
printf("rk_aiq_uapi_sysctl_stop enter\n");
rk_aiq_uapi_sysctl_stop(g_aiq_ctx, false);
printf("rk_aiq_uapi_sysctl_deinit enter\n");
rk_aiq_uapi_sysctl_deinit(g_aiq_ctx);
printf("rk_aiq_uapi_sysctl_deinit exit\n");
g_aiq_ctx = NULL;
}
RK_S32 SAMPLE_COMM_ISP_Run(void) {
if (!g_aiq_ctx)
return -1;
if (rk_aiq_uapi_sysctl_start(g_aiq_ctx)) {
printf("rk_aiq_uapi_sysctl_start failed\n");
return -1;
}
printf("rk_aiq_uapi_sysctl_start succeed\n");
return 0;
}
RK_VOID SAMPLE_COMM_ISP_DumpExpInfo(rk_aiq_working_mode_t WDRMode) {
char aStr[128] = {'\0'};
Uapi_ExpQueryInfo_t stExpInfo;
rk_aiq_wb_cct_t stCCT;
rk_aiq_user_api_ae_queryExpResInfo(g_aiq_ctx, &stExpInfo);
rk_aiq_user_api_awb_GetCCT(g_aiq_ctx, &stCCT);
if (WDRMode == RK_AIQ_WORKING_MODE_NORMAL) {
sprintf(aStr, "M:%.0f-%.1f LM:%.1f CT:%.1f",
stExpInfo.CurExpInfo.LinearExp.exp_real_params.integration_time *
1000 * 1000,
stExpInfo.CurExpInfo.LinearExp.exp_real_params.analog_gain,
stExpInfo.MeanLuma, stCCT.CCT);
} else {
sprintf(aStr, "S:%.0f-%.1f M:%.0f-%.1f L:%.0f-%.1f SLM:%.1f MLM:%.1f "
"LLM:%.1f CT:%.1f",
stExpInfo.CurExpInfo.HdrExp[0].exp_real_params.integration_time *
1000 * 1000,
stExpInfo.CurExpInfo.HdrExp[0].exp_real_params.analog_gain,
stExpInfo.CurExpInfo.HdrExp[1].exp_real_params.integration_time *
1000 * 1000,
stExpInfo.CurExpInfo.HdrExp[1].exp_real_params.analog_gain,
stExpInfo.CurExpInfo.HdrExp[2].exp_real_params.integration_time *
1000 * 1000,
stExpInfo.CurExpInfo.HdrExp[2].exp_real_params.analog_gain,
stExpInfo.HdrMeanLuma[0], stExpInfo.HdrMeanLuma[1],
stExpInfo.HdrMeanLuma[2], stCCT.CCT);
}
printf("isp exp dump: %s\n", aStr);
}
RK_VOID SAMPLE_COMM_ISP_SetFrameRate(RK_U32 uFps) {
if (!g_aiq_ctx)
return;
printf("SAMPLE_COMM_ISP_SetFrameRate start %d\n", uFps);
frameRateInfo_t info;
info.mode = OP_MANUAL;
info.fps = uFps;
rk_aiq_uapi_setFrameRate(g_aiq_ctx, info);
printf("SAMPLE_COMM_ISP_SetFrameRate %d\n", uFps);
}
RK_VOID SAMPLE_COMM_ISP_SetLDCHLevel(RK_U32 level) {
if (!g_aiq_ctx)
return;
rk_aiq_uapi_setLdchEn(g_aiq_ctx, level > 0);
if (level > 0 && level <= 255)
rk_aiq_uapi_setLdchCorrectLevel(g_aiq_ctx, level);
}
/*only support switch between HDR and normal*/
RK_VOID SAMPLE_COMM_ISP_SetWDRModeDyn(rk_aiq_working_mode_t WDRMode) {
rk_aiq_uapi_sysctl_swWorkingModeDyn(g_aiq_ctx, WDRMode);
}
#endif
sqlite3_opencv_image.cpp
#include "sqlite3_operation.h"
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgcodecs/legacy/constants_c.h>
#include <vector>
#include <fstream>
using namespace cv;
int main()
{
Connection_sqlite3DataBase();
#if 0
//vector<char> images = QueryImageData();
People people = QueryImageData();
printf("peope_name = %s, image_size = %d\n",people.people_name.c_str(),people.images.size());
cv::Mat image_mat = cv::imdecode(people.images, CV_LOAD_IMAGE_COLOR);
if (image_mat.empty())
{
printf("failed to decode image\n");
return -1;
}
cv::imwrite("blob_output.jpg", image_mat);
#endif
//map<People, rockx_face_feature_t> people_map = QueryPeopleData();
#if 0
ifstream ifs("chenzhi02.png", ios::binary | ios::ate);
int file_size = ifs.tellg();
ifs.seekg(0, ios::beg);
vector<char> buffer(file_size);
ifs.read(buffer.data(), file_size);
ifs.close();
// 解码图像
Mat img = imdecode(Mat(buffer), IMREAD_COLOR);
if (img.empty())
{
printf("failed to decode image\n");
return -1;
}
imwrite("output_blog.jpg",img);
#endif
#if 0
vector<char> images = QueryImageData();
vector<unsigned char> img_encode;
printf("image_size = %d\n", images.size());
cv::imencode(".jpg", images, img_encode);
Mat img = imdecode(Mat(img_encode), IMREAD_COLOR);
if (img.empty())
{
printf("failed to decode image\n");
return -1;
}
imwrite("output_blog_02.jpg",img);
#endif
#if 0
FILE *imageFile = fopen("chenzhi02.png", "rb"); // 打开名为image.jpg的图片文件(二进制模式)
if (!imageFile)
{
printf("无法打开图片文件.\n");
return -1;
}
fseek(imageFile, 0L, SEEK_END); // 定位到文件尾部获取文件大小
long fileSize = ftell(imageFile); // 获取文件大小
rewind(imageFile); // 重新定位到文件起始位置
printf("fileSize = %ld\n", fileSize);
unsigned char *buffer = new unsigned char[fileSize]; // 分配足够大小的缓冲区
size_t bytesRead = fread(buffer, sizeof(unsigned char), fileSize, imageFile); // 从文件中读取图像数据到缓冲区
printf("bytesRead = %d\n", bytesRead);
fclose(imageFile);
const char * name = "harry";
float feature[512] ={};
insert_face_data_toDataBase(name,feature,512,buffer,bytesRead);
#endif
return 0;
}
sqlite3_operation.cpp
#include "sqlite3_operation.h"
#include <opencv2/core/hal/interface.h>
#include <iostream>
#include <fstream>
sqlite3 *db = NULL;
char zErrMsg = 0;
int rc, id;
sqlite3_stmt *stmt;
/*!
* \fn Connection_sqlite3DataBase
* \brief �������ݿ�
*
*
* \retval int
*/
int Connection_sqlite3DataBase()
{
rc = sqlite3_open("/userdata/face.db", &db);
if (rc != SQLITE_OK)
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
else
{
printf("You have opened a sqlite3 database named bind.db successfully!\nCongratulation! Have fun!\n");
}
return 0;
}
/*!
* \fn insert_face_data_toDataBase
* \brief �������ݿ�
*
* \param [in] const char * name #
* \param [in] float feature [ 512 ] #
* \param [in] int featureSize #
*
* \retval void
*/
void insert_face_data_toDataBase(const char *name, float feature[512], int featureSize, uint8_t *image_data, int image_size)
{
printf("face_size = %d\n", image_size);
sqlite3_prepare(db, "insert into face_data_table(name,face_feature,feature_size,image_data,image_size) values (?,?,?,?,?);", -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, name, strlen(name), NULL);
sqlite3_bind_blob(stmt, 2, feature, featureSize, NULL);
sqlite3_bind_int(stmt, 3, featureSize);
sqlite3_bind_blob(stmt, 4, image_data, image_size, NULL);
sqlite3_bind_int(stmt, 5, image_size);
printf("insert face feature: ");
for (int i = 0; i < 50; i++)
{
printf("%f ", feature[i]);
}
printf("\n");
sqlite3_step(stmt);
}
/*!
* \fn QueryFaceFeature
* \brief ��ѯ��������
*
*
* \retval map<string, rockx_face_feature_t>
*/
map<string, rockx_face_feature_t> QueryFaceFeature()
{
rockx_face_feature_t rockx_face_feature = {0, 0};
map<string, rockx_face_feature_t> rockx_face_feature_map;
sqlite3_stmt *stmt;
char *sql = "select name, feature_size, face_feature from face_data_table";
int ret = sqlite3_prepare(db, sql, strlen(sql), &stmt, 0);
int id = 0, len = 0;
char *name;
int feature_size;
if (ret == SQLITE_OK)
{
while (sqlite3_step(stmt) == SQLITE_ROW)
{
name = (char *)sqlite3_column_text(stmt, 0);
printf("name = %s\n", name);
feature_size = sqlite3_column_int(stmt, 1);
printf("feature_size = %d\n", feature_size);
const void *feature = sqlite3_column_blob(stmt, 2);
memset(rockx_face_feature.feature, 0, feature_size);
memcpy(rockx_face_feature.feature, feature, feature_size);
printf("feature: ");
for (int i = 0; i < 50; i++)
{
printf("%f ", rockx_face_feature.feature[i]);
}
printf("\n");
rockx_face_feature.len = feature_size;
string str(name);
rockx_face_feature_map.insert(pair<string, rockx_face_feature_t>(str, rockx_face_feature));
}
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return rockx_face_feature_map;
}
map<People, rockx_face_feature_t> QueryPeopleData()
{
rockx_face_feature_t rockx_face_feature = {0, 0};
map<People, rockx_face_feature_t> people_map;
sqlite3_stmt *stmt;
char *sql = "select name, feature_size, face_feature, image_size, image_data from face_data_table";
int id = 0, len = 0;
char *name;
int feature_size;
int image_size;
vector<char> images;
People first_people;
int ret = sqlite3_prepare(db, sql, strlen(sql), &stmt, 0);
if (ret == SQLITE_OK)
{
while (sqlite3_step(stmt) == SQLITE_ROW)
{
name = (char *)sqlite3_column_text(stmt, 0);
printf("name = %s\n", name);
feature_size = sqlite3_column_int(stmt, 1);
printf("feature_size = %d\n", feature_size);
const void *feature = sqlite3_column_blob(stmt, 2);
memset(rockx_face_feature.feature, 0, feature_size);
memcpy(rockx_face_feature.feature, feature, feature_size);
rockx_face_feature.len = feature_size;
image_size = sqlite3_column_int(stmt, 3);
printf("image_size = %d\n", image_size);
const char * image_data = (const char *)sqlite3_column_blob(stmt, 4);
for (int i = 0; i < image_size; i++)
{
images.push_back(image_data[i]);
}
first_people.people_name = string(name);
first_people.images = images;
string str(name);
//people_map.insert(pair<const People, rockx_face_feature_t>(first_people, rockx_face_feature));
people_map.insert(make_pair(first_people, rockx_face_feature));
//people_map.insert(first_people, rockx_face_feature);
}
}
return people_map;
}
#if 0
vector<char> QueryImageData()
{
vector<char> images;
sqlite3_stmt *stmt;
char *sql = "select image_data, image_size from face_data_table";
int ret = sqlite3_prepare(db, sql, strlen(sql), &stmt, 0);
int id = 0, len = 0;
char *name;
int feature_size;
const char *feature;
FILE *png_file = fopen("image_output.jpg", "w+");
if (ret == SQLITE_OK)
{
printf("ssdsdasda\n");
while (sqlite3_step(stmt) == SQLITE_ROW)
{
printf("ssssss\n");
feature = (const char *)sqlite3_column_blob(stmt, 0);
feature_size = sqlite3_column_int(stmt, 1);
// printf("feature_size = %d\n", feature_size);
// fwrite(feature, sizeof(char), 18064, png_file);
for (int i = 0; i < feature_size; i++)
{
images.push_back(feature[i]);
}
// images(feature, feature + 18064);
// memset(rockx_face_feature.feature, 0, feature_size);
// memcpy(rockx_face_feature.feature, feature, feature_size);
}
}
return images;
}
#endif
struct People QueryImageData()
{
struct People people;
vector<char> images;
sqlite3_stmt *stmt;
char *sql = "select name, image_data, image_size from face_data_table";
int ret = sqlite3_prepare(db, sql, strlen(sql), &stmt, 0);
int id = 0, len = 0;
char *name;
int feature_size;
const char *feature;
if (ret == SQLITE_OK)
{
printf("ssdsdasda\n");
while (sqlite3_step(stmt) == SQLITE_ROW)
{
printf("ssssss\n");
name = (char *)sqlite3_column_text(stmt, 0);
feature = (const char *)sqlite3_column_blob(stmt, 1);
feature_size = sqlite3_column_int(stmt, 2);
for (int i = 0; i < feature_size; i++)
{
images.push_back(feature[i]);
}
string name_str(name);
people.people_name = name_str;
people.images = images;
}
}
return people;
}
sqlite3_operation_test.cpp
*
* ./sqlite3_operation_test name image_path
*/
#include "rockx.h"
#include "sqlite3_operation.h"
#define FEATURE_SIZE 512
rockx_handle_t face_det_handle;
rockx_handle_t face_5landmarks_handle;
rockx_handle_t face_recognize_handle;
rockx_object_t *get_max_face(rockx_object_array_t *face_array)
{
if (face_array->count == 0)
{
return NULL;
}
rockx_object_t *max_face = NULL;
int i;
for (i = 0; i < face_array->count; i++)
{
rockx_object_t *cur_face = &(face_array->object[i]);
if (max_face == NULL)
{
max_face = cur_face;
continue;
}
int cur_face_box_area = (cur_face->box.right - cur_face->box.left) *
(cur_face->box.bottom - cur_face->box.top);
int max_face_box_area = (max_face->box.right - max_face->box.left) *
(max_face->box.bottom - max_face->box.top);
if (cur_face_box_area > max_face_box_area)
{
max_face = cur_face;
}
}
printf("get_max_face %d\n", i - 1);
return max_face;
}
int run_face_recognize(const char *name, rockx_image_t *in_image, rockx_face_feature_t *out_feature, unsigned char * buffer, int buffer_size)
{
rockx_ret_t ret;
/*************** FACE Detect ***************/
// create rockx_face_array_t for store result
rockx_object_array_t face_array;
memset(&face_array, 0, sizeof(rockx_object_array_t));
// detect face
ret = rockx_face_detect(face_det_handle, in_image, &face_array, NULL);
if (ret != ROCKX_RET_SUCCESS)
{
printf("rockx_face_detect error %d\n", ret);
return -1;
}
#if 0
// process result
for (int i = 0; i < face_array.count; i++)
{
int left = face_array.object[i].box.left;
int top = face_array.object[i].box.top;
int right = face_array.object[i].box.right;
int bottom = face_array.object[i].box.bottom;
float score = face_array.object[i].score;
printf("%d box=(%d %d %d %d) score=%f\n", i, left, top, right, bottom,
score);
}
#endif
rockx_object_t *max_face = get_max_face(&face_array);
if (max_face == NULL)
{
printf("error no face detected\n");
return -1;
}
// Face Align
rockx_image_t out_img;
memset(&out_img, 0, sizeof(rockx_image_t));
ret = rockx_face_align(face_5landmarks_handle, in_image, &(max_face->box), NULL, &out_img);
if (ret != ROCKX_RET_SUCCESS)
{
return -1;
}
// Face Recognition
rockx_face_recognize(face_recognize_handle, &out_img, out_feature);
insert_face_data_toDataBase(name, out_feature->feature, FEATURE_SIZE, buffer, buffer_size);
// Release Aligned Image
rockx_image_release(&out_img);
return 0;
}
int main(int argc, char *argv[])
{
rockx_ret_t ret;
printf("----------------- main init ---------------------\n");
#if 1
if (argc != 3)
{
printf("Usage: Insert DataBase ./sqlite3_operation_test name image_path\n");
return -1;
}
printf("Start Connection sqlite3......................\n");
Connection_sqlite3DataBase();
printf("End Connection_sqlite3DataBase......................\n");
rockx_config_t *config = rockx_create_config();
rockx_add_config(config, ROCKX_CONFIG_DATA_PATH, "/userdata/rockx_data/");
/*************** Creat Handle ***************/
// create a face detection handle
ret = rockx_create(&face_det_handle, ROCKX_MODULE_FACE_DETECTION_V2, config, 0);
if (ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_DETECTION error %d\n", ret);
return -1;
}
// create a face landmark handle
ret = rockx_create(&face_5landmarks_handle, ROCKX_MODULE_FACE_LANDMARK_5,
config, 0);
if (ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_LANDMARK_68 error %d\n", ret);
return -1;
}
// create a face recognize handle
ret = rockx_create(&face_recognize_handle, ROCKX_MODULE_FACE_RECOGNIZE,
config, 0);
if (ret != ROCKX_RET_SUCCESS)
{
printf("init rockx module ROCKX_MODULE_FACE_RECOGNIZE error %d\n", ret);
return -1;
}
const char *name = argv[1];
const char *image_path = argv[2];
rockx_face_feature_t out_feature1;
rockx_image_t input_image1;
rockx_image_read(image_path, &input_image1, 1);
FILE *imageFile = fopen(image_path, "rb"); // 打开名为image.jpg的图片文件(二进制模式)
if (!imageFile)
{
printf("无法打开图片文件.\n");
return -1;
}
fseek(imageFile, 0L, SEEK_END); // 定位到文件尾部获取文件大小
long fileSize = ftell(imageFile); // 获取文件大小
rewind(imageFile); // 重新定位到文件起始位置
printf("fileSize = %ld\n", fileSize);
unsigned char *buffer = new unsigned char[fileSize]; // 分配足够大小的缓冲区
size_t bytesRead = fread(buffer, sizeof(unsigned char), fileSize, imageFile); // 从文件中读取图像数据到缓冲区
run_face_recognize(name, &input_image1, &out_feature1, buffer, bytesRead);
#endif
return 0;
}
douyin_rv1126_rockx_face_project文件夹
编译完后
打开服务器
使用程序sqlite3_operation_test zhangjiahao face_02.jpg创建数据库
执行程序代码:./rv1126_ffmpeg_main_rockx 0 rtmp://192.168.100.80:1935/live/01
更多推荐

所有评论(0)