OPENCV——RV1126+OPENCV在视频中添加LOGO图像
前面我们学的都是在图片上进行操作但是在真正用的时候都是在视频上下面就是在视频上处理一、大体流程本章节主要是利用RV1126的视频流结合OPENCV的API在视频流里面添加LOGO图像换言之就是在RV1126的视频流里面叠加图片。大体流程我们来看上图要完成这个功能我们需要创建两个线程(实际上还有初始化过程前面的博客讲了好多次这里先忽略了)第一个线程是opencv_vi_logo_handle_thread它主要是获取VI原始数据并有OPENCV转换成Mat矩阵然后添加LOGO图像并把VI数据发送到VENC编码器。第二个线程是get_venc_stream_thread它主要是获取H264的VENC码流数据并且保存到H264文件。其实本质就是在每一帧图片上copyTo二、具体代码实现上图我们已经说了大概的流程图这部分我们重点讲解代码的实现// 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. #include assert.h #include bits/types/FILE.h #include fcntl.h #include getopt.h #include opencv2/imgproc.hpp #include pthread.h #include signal.h #include stdbool.h #include stdio.h #include stdlib.h #include time.h #include unistd.h // #include common/sample_common.h #include rkmedia_api.h #include opencv2/core.hpp // #include opencv2/imgoroc.hpp #include opencv2/highgui.hpp #include opencv2/opencv.hpp using namespace cv; using namespace std; #define CAMERA_PATH rkispp_scale0 #define CAMERA_ID 0 #define CAMERA_CHN 0 #define VENC_CHN 0 #define WIDTH 1920 #define HEIGHT 1080 void * opencv_vi_logo_handle_thread(void * args) { pthread_detach(pthread_self()); MEDIA_BUFFER mb; Mat logo_img imread(/userdata/OIP-C.png); cvtColor(logo_img, logo_img, COLOR_RGB2GRAY); while(1) { mb RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, CAMERA_CHN, -1); if(!mb) { printf(Get vi break...\n); break; } printf(Get vi success...\n); Mat rv1126_img_mat Mat(HEIGHT, WIDTH, CV_8UC1, RK_MPI_MB_GetPtr(mb)); Mat rv1126_img_mat_roi rv1126_img_mat(Rect(100,100,logo_img.cols,logo_img.rows)); logo_img.copyTo(rv1126_img_mat_roi); RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, VENC_CHN, mb); RK_MPI_MB_ReleaseBuffer(mb); } return NULL; } void * get_venc_stream_thread(void * args) { pthread_detach(pthread_self()); MEDIA_BUFFER mb NULL; FILE * h264_opencv_logo_file fopen(test_opencvlogo.h264, w); while(1) { mb RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC,VENC_CHN, -1); if(!mb) { printf(Get venc break...\n); break; } fwrite(RK_MPI_MB_GetPtr(mb),RK_MPI_MB_GetSize(mb), 1, h264_opencv_logo_file); RK_MPI_MB_ReleaseBuffer(mb); } return NULL; } int main() { int ret; VI_CHN_ATTR_S vi_chn_attr; vi_chn_attr.pcVideoNode CAMERA_PATH; // Path vi_chn_attr.u32Width WIDTH; // Width vi_chn_attr.u32Height HEIGHT; // 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 WIDTH; venc_chn_attr.stVencAttr.u32PicHeight HEIGHT; venc_chn_attr.stVencAttr.u32VirWidth WIDTH; venc_chn_attr.stVencAttr.u32VirHeight HEIGHT; 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 WIDTH * HEIGHT * 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(VENC_CHN, 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(start vi failed....\n); } else { printf(start vi success....\n); } pthread_t pid1, pid2; pthread_create(pid1, NULL, opencv_vi_logo_handle_thread, NULL); pthread_create(pid2, NULL, get_venc_stream_thread, NULL); while (1) { sleep(2); } RK_MPI_VENC_DestroyChn(VENC_CHN); RK_MPI_VI_DisableChn(CAMERA_ID, CAMERA_CHN); return 0; }2.1. RV1126模块初始化并启动VI工作RV1126模块的初始化包括VI模块的初始化(RK_MPI_VI_SetChnAttr)、使能VI模块(RK_MPI_VI_EnableChn)、VENC模块的初始化(RK_MPI_VENC_CreateChn)、启动VI工作(RK_MPI_VI_StartStream)。关于这方面的参数设置我们就不详细说了因为这方面的内容之前的课程已经详细说过。2.2. opencv_vi_handle_thread线程的讲解void * opencv_vi_logo_handle_thread(void * args) { pthread_detach(pthread_self()); MEDIA_BUFFER mb; Mat logo_img imread(/userdata/OIP-C.png); cvtColor(logo_img, logo_img, COLOR_RGB2GRAY); while(1) { mb RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, CAMERA_CHN, -1); if(!mb) { printf(Get vi break...\n); break; } printf(Get vi success...\n); Mat rv1126_img_mat Mat(HEIGHT, WIDTH, CV_8UC1, RK_MPI_MB_GetPtr(mb)); Mat rv1126_img_mat_roi rv1126_img_mat(Rect(100,100,logo_img.cols,logo_img.rows)); logo_img.copyTo(rv1126_img_mat_roi); RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, VENC_CHN, mb); RK_MPI_MB_ReleaseBuffer(mb); } return NULL; }上面是opencv_vi_logo_handle_thread的具体实现。首先我们要通过imread读取图片然后把图片cvtColor转换成灰度图片(由于VI模块的图像格式是NV12所以我们的图片必须要以灰度图的方式进行添加)。然后调用RK_MPI_SYS_GetMediaBuffer获取每一帧的VI视频原始数据然后使用OPENCV的API把每一个视频数据转换成Mat矩阵具体的操作是Mat rv1126_img_mat Mat(HEIGHT, WIDTH, CV_8UC1, RK_MPI_MB_GetPtr(mb))这是一个Mat构造器第一个参数是HEIGHT1080第二个参数WIDTH1920第三个参数图像格式CV_8UC1第四个参数:具体的图像数据RK_MPI_MB_GetPtr(mb)。通过Mat的构造器就可以把RV1126的VI视频数据转换成Mat转换成Mat之后我们就需要对Mat进行图层叠加操作。Mat叠加操作需要分两步第一步先创建一个感兴趣区域Mat rv1126_img_mat_roi rv1126_img_mat(Rect(100, 100, logo_img.cols, logo_img.rows))这里的感兴趣区域以矩形为背景Rect(100,100,logo_img.cols,logo_img.rows)x:100y: 100width:logo_img.colsheight:logo_img.rows。第二步利用copyTo函数把读取的图片拷贝到感兴趣区域rv1126_img_mat_roi ,具体代码是logo_img.copyTo(rv1126_img_mat_roi)。进行上述所有的操作后就需要把RV1126叠加过后的视频VI数据发送到H264的VENC编码器调用的API是RK_MPI_SYS_SendMediaBuffer。2.3. get_venc_stream_thread线程的讲解void * get_venc_stream_thread(void * args) { pthread_detach(pthread_self()); MEDIA_BUFFER mb NULL; FILE * h264_opencv_logo_file fopen(test_opencvlogo.h264, w); while(1) { mb RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC,VENC_CHN, -1); if(!mb) { printf(Get venc break...\n); break; } fwrite(RK_MPI_MB_GetPtr(mb),RK_MPI_MB_GetSize(mb), 1, h264_opencv_logo_file); RK_MPI_MB_ReleaseBuffer(mb); } return NULL; }上面是get_venc_stream_thread的具体实现在这个线程里面要通过RK_MPI_SYS_GetMediaBuffer获取每一帧H264的编码数据然后用fwrite写入。2.4. 输出结果经过上面的编码后我们来看看输出的H264文件。可以看到这个H264文件嵌入了周董的JPG图片。这个效果就实现了用OPENCV图片叠加的功能对RV1126的视频流进行图片LOGO的添加