rv1126物体检测 rkmedia、opencv……

整体码流流向:

因此代码也分为这几部分:

VI:采集视频 配置视频采集信息

模型推理线程:获取VI码流、载入模型、进行模型推理、保存推理结果

画框线程:获取VI码流、获取推理结果、显示推理结果、输出码流到VENC

VENC:编码

RTSP:获取VENC编码码流、传输

代码分析:

main函数:

从总体来看 main函数还是很简单的,首先创建了一个rknn队列,然后配置摄像头节点、初始化rtsp(默认端口554)(rtsp在主函数进行创建 rtsp 实例、创建 rtsp 接口、设置 rtsp 传输属性、同步 rtsp 时间戳然后在真正推流时候即线程中更新视频流设置并驱动事件即可)

后面就是对VI以及VENC的一些配置,然后创建了三个线程,这三个线程的创建是很重要的

int main(int argc, char *argv[])
{rknn_queue = new RKNN_QUEUE();RK_MPI_SYS_Init();RK_U32 u32Width = 1920;RK_U32 u32Height = 1080;RK_CHAR *pDeviceName_01 = "rkispp_scale0";RK_CHAR *pDeviceName_02 = "rkispp_scale1";RK_CHAR *pDeviceName_03 = "rkispp_m_bypass";RK_CHAR *pOutPath = "test_output.h264";RK_CHAR *pIqfilesPath = NULL;CODEC_TYPE_E enCodecType = RK_CODEC_TYPE_H264;RK_CHAR *pCodecName = "H264";RK_S32 s32CamId = 0;RK_U32 u32BufCnt = 3;int ret;printf("#Device: %s\n", pDeviceName_01);printf("#CodecName:%s\n", pCodecName);printf("#Resolution: %dx%d\n", u32Width, u32Height);printf("#Frame Count to save: %d\n", g_s32FrameCnt);printf("#Output Path: %s\n", pOutPath);printf("#CameraIdx: %d\n\n", s32CamId);g_rtsplive = create_rtsp_demo(554);g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/main_stream");rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());#if 1VI_CHN_ATTR_S vi_chn_attr;vi_chn_attr.pcVideoNode = pDeviceName_01;vi_chn_attr.u32BufCnt = u32BufCnt;vi_chn_attr.u32Width = u32Width;vi_chn_attr.u32Height = u32Height;vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;ret = RK_MPI_VI_SetChnAttr(s32CamId, 0, &vi_chn_attr);ret |= RK_MPI_VI_EnableChn(s32CamId, 0);if (ret){printf("ERROR: create rkisp0 VI[0] error! ret=%d\n", ret);return 0;}
#endifVENC_CHN_ATTR_S venc_chn_attr;memset(&venc_chn_attr, 0, sizeof(VENC_CHN_ATTR_S));venc_chn_attr.stVencAttr.u32PicWidth = 1920;venc_chn_attr.stVencAttr.u32PicHeight = 1080;venc_chn_attr.stVencAttr.u32VirWidth = 1920;venc_chn_attr.stVencAttr.u32VirHeight = 1080;venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;venc_chn_attr.stVencAttr.u32Profile = 66;venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 30;venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = 1920 * 1080 * 3;venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr);if (ret){printf("ERROR: Create venc failed!\n");exit(0);}ret = RK_MPI_VI_StartStream(s32CamId, 0);if (ret){printf("Start VI[0] failed! ret=%d\n", ret);return -1;}//pthread_t vi_pid;pthread_t rknn_pid;pthread_t process_pid;pthread_t rtsp_pid;pthread_create(&rknn_pid, NULL, rknn_vi_thread, NULL);pthread_create(&process_pid, NULL, rknn_vi_rknn_thread, NULL);pthread_create(&rtsp_pid, NULL, rknn_venc_rtsp_thread, NULL);printf("%s initial finish\n", __func__);signal(SIGINT, sigterm_handler);while (!quit){usleep(500000);}if (g_output_file)fclose(g_output_file);printf("%s exit!\n", __func__);// destroy venc before viret = RK_MPI_VENC_DestroyChn(0);if (ret){printf("ERROR: Destroy VENC[0] error! ret=%d\n", ret);return 0;}// destroy viret = RK_MPI_VI_DisableChn(s32CamId, 0);if (ret){printf("ERROR: Destroy VI[0] error! ret=%d\n", ret);return 0;}ret = RK_MPI_RGA_DestroyChn(0);if (ret){printf("ERROR: Destroy RGA[0] error! ret=%d\n", ret);return 0;}if (pIqfilesPath){
#if 0SAMPLE_COMM_ISP_Stop(s32CamId);
#endif}
}

线程1:

        首先pthread_detach使得子线程与主线程分离且结束时候自动回收资源

        然后计算出比率比如vi采集的宽为1000,而模型推理输入的图片宽为100那么比例就是1000/10=10 那么model10的位置就是vi100的位置了

        下面进入循环,首先从VI通道0阻塞的方式获取码流,然后把该码流拷贝到&Media_Buffer[0]所指向的位置即该数组的首地址

        下面使用主函数创建的rknn队列(rknn_queue查看识别结果),并使用opencv创建了一个方框其高为1080宽为1920 CV_8UC1: 8位无符号单通道图像(灰度图像),显示在VI图像上

        从获取的结果中根据识别到的物体数量循环,如果置信度小于0.7则退出,如果大于0.7,分别转换得到1920*1080图片的坐标

        在原图mb上进行画框,并在框框对应位置载入文字(识别结果)

        最后将处理后的数据传输给VENC并销毁资源

        总结一下画框和文字显示,画框其实是直接在图片上画框了,但是文字显示其实是先在图像上创建了一个和图片一样大的框框(矩阵),然后对这个框框的对应位置进行了文字显示,由于框框在图片上因此文字就显示在了图片上

//这个线程用来绘制框框
void *rknn_vi_thread(void *args)
{pthread_detach(pthread_self());int frame_id = 0;MEDIA_BUFFER mb = NULL;
//我个人认为这里MODEL_INPUT_SIZE是模型训练时用的输入尺寸float x_rate = (float)1920.0 / MODEL_INPUT_SIZE;float y_rate = (float)1080.0 / MODEL_INPUT_SIZE;printf("x_rate is %f, y_rate is %f\n", x_rate, y_rate);while (!quit){mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 0, -1);if (!mb){printf("RK_MPI_SYS_GetMediaBuffer get null buffer!\n");break;}memcpy(&Media_Buffer[0], (uint8_t *)RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb));Media_Buffer_size = RK_MPI_MB_GetSize(mb);detect_result_group_t detect_result_group = rknn_queue->getDetectResult();Mat orig_img = Mat(1080, 1920, CV_8UC1, RK_MPI_MB_GetPtr(mb));for (int j = 0; j < detect_result_group.count; j++){/*if (strcmp(detect_result_group.results[j].name, "person"))continue;*/if (detect_result_group.results[j].prop < 0.7)continue;int x = detect_result_group.results[j].box.left * x_rate;int y = detect_result_group.results[j].box.top * y_rate;int w = (detect_result_group.results[j].box.right -detect_result_group.results[j].box.left) *x_rate;int h = (detect_result_group.results[j].box.bottom -detect_result_group.results[j].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;}printf("border=(%d %d %d %d)\n", x, y, w, h);nv12_border((char *)RK_MPI_MB_GetPtr(mb), 1920, 1080, x, y, w, h, 0, 0, 255);putText(orig_img, detect_result_group.results[j].name, Point(x + 32, y + 64), 2, 3, Scalar(255, 0, 0, 255));}RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, 0, mb);RK_MPI_MB_ReleaseBuffer(mb);}
}

线程2:

        首先创建rknn模型连接符 ctx(类似于文件的fd) 然后使用load_model载入模型,这个函数内部也就是一个fopen函数 model_len是该函数执行后得到的模型长度

        rknn载入模型,把ctx与该模型连接起来

        下面这一段其实是判断了输入数与输出数其实可以不要 rknn_query 可以得到 输入数与输出数也可以得到其具体属性等等,具体可以参考瑞芯微 rknn手册

        一直到下面计算x,y比例(和上一个线程一样),然后创建了一个数据结构体

        进入while()循环,首先根据上述结构体创建了图像数据缓冲区mb,然后把上一线程中Media_Buffer(即vi获得的图像)拷贝到mb中

        设置原始图像字节数1920*1080*3通道与模型图片字节300*300*3,先将采集的NV12图像转化为rgb888,然后再将输入图像转化为300*300*3

        下面对模型输入输出进行配置:输入:图像数据 输出:名称与置信度

        运行模型rknn_run 进行一次推理

        最后对ssd预测做了一个后处理并把处理结果存在了detect_result_group,并把detect_result_group存入队列(这里是put,线程1 是get获取)

        释放资源

void *rknn_vi_rknn_thread(void *args)
{pthread_detach(pthread_self());//rknn_context感觉类似于fd文件描述符一样,有点像模型描述符rknn_context ctx;int ret;int model_len = 0;unsigned char *model;printf("Loading model ...\n");model = load_model(g_ssd_path, &model_len);ret = rknn_init(&ctx, model, model_len, 0);if (ret < 0){printf("rknn_init fail! ret=%d\n", ret);return NULL;}// Get Model Input Output Inforknn_input_output_num io_num;ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));if (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return NULL;}printf("model input num: %d, output num: %d\n", io_num.n_input,io_num.n_output);printf("input tensors:\n");rknn_tensor_attr input_attrs[io_num.n_input];memset(input_attrs, 0, sizeof(input_attrs));for (unsigned int i = 0; i < io_num.n_input; i++){input_attrs[i].index = i;ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]),sizeof(rknn_tensor_attr));if (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return NULL;}printRKNNTensor(&(input_attrs[i]));}printf("output tensors:\n");rknn_tensor_attr output_attrs[io_num.n_output];memset(output_attrs, 0, sizeof(output_attrs));for (unsigned int i = 0; i < io_num.n_output; i++){output_attrs[i].index = i;ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]),sizeof(rknn_tensor_attr));if (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return NULL;}printRKNNTensor(&(output_attrs[i]));}rga_buffer_t src;float x_rate = (float)1920 / MODEL_INPUT_SIZE;float y_rate = (float)1080 / MODEL_INPUT_SIZE;MB_IMAGE_INFO_S stImageInfo = {1920, 1080, 1920, 1080, IMAGE_TYPE_NV12};while (!quit){MEDIA_BUFFER mb = RK_MPI_MB_CreateImageBuffer(&stImageInfo, RK_TRUE, MB_FLAG_NOCACHED);if (!mb){printf("ERROR: no space left!\n");break;}//Media_Buffer在上一线程是获取的vi视频流memcpy(RK_MPI_MB_GetPtr(mb), Media_Buffer, Media_Buffer_size);RK_MPI_MB_SetSize(mb, Media_Buffer_size);int rga_buffer_size = 1920 * 1080 * 3;int rga_buffer_model_input_size = MODEL_INPUT_SIZE * MODEL_INPUT_SIZE * 3;unsigned char *rga_buffer = (unsigned char *)malloc(rga_buffer_size);unsigned char *rga_buffer_model_input =(unsigned char *)malloc(rga_buffer_model_input_size);nv12_to_rgb24((unsigned char *)RK_MPI_MB_GetPtr(mb), rga_buffer, 1920,1080);
//这几步 把vi码流数据转换为模型推理格式rgb 888 300x300rgb24_resize(rga_buffer, rga_buffer_model_input, 1920, 1080, MODEL_INPUT_SIZE, MODEL_INPUT_SIZE);rknn_input inputs[1];memset(inputs, 0, sizeof(inputs));inputs[0].buf = rga_buffer_model_input;inputs[0].index = 0;inputs[0].type = RKNN_TENSOR_UINT8;inputs[0].size = rga_buffer_model_input_size;inputs[0].fmt = RKNN_TENSOR_NHWC;//设置模型推理输入ret = rknn_inputs_set(ctx, io_num.n_input, inputs);if (ret < 0){printf("rknn_input_set fail! ret=%d\n", ret);return NULL;}// Runprintf("rknn_run\n");//执行一次模型推理。ret = rknn_run(ctx, NULL);if (ret < 0){printf("rknn_run fail! ret=%d\n", ret);return NULL;}rknn_output outputs[2];memset(outputs, 0, sizeof(outputs));outputs[0].want_float = 1;outputs[1].want_float = 1;ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL);if (ret < 0){printf("rknn_outputs_get fail! ret=%d\n", ret);return NULL;}
//推理结果detect_result_group/detect_result_group_t detect_result_group;postProcessSSD((float *)(outputs[0].buf), (float *)(outputs[1].buf), MODEL_INPUT_SIZE,MODEL_INPUT_SIZE, &detect_result_group);//存放推理结果rknn_queue->putDetectResult(detect_result_group);RK_MPI_MB_ReleaseBuffer(mb);mb = NULL;if (rga_buffer != NULL){free(rga_buffer);rga_buffer = NULL;}if (rga_buffer_model_input != NULL){free(rga_buffer_model_input);rga_buffer_model_input = NULL;}}return NULL;
}

线程3:

        分离线程

        循环中不断进行推流即:rtsp_tx_video

        释放资源

        删除rtsp实例:rtsp_do_event

void *rknn_venc_rtsp_thread(void *args)
{pthread_detach(pthread_self());MEDIA_BUFFER mb = NULL;while (!quit){mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, 0, -1);if (!mb){printf("RK_MPI_SYS_GetMediaBuffer venc get null buffer!\n");break;}rtsp_tx_video(g_rtsp_session, (unsigned char *)RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), RK_MPI_MB_GetTimestamp(mb));RK_MPI_MB_ReleaseBuffer(mb);rtsp_do_event(g_rtsplive);}return NULL;
}

最后在Ubuntu端进行rtsp拉流处理

ffplay –x 400 –y 400 rtsp://192.168.1.22:554/live/main_stream

-x -y不必多说是窗口大小 

192.168.1.22是开发板即推流端IP 

554是前面设置的端口号

/live/main_stream也是前面设置的流的路径

效果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/49006.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用C#快速创建定时任务

原文链接&#xff1a;https://www.cnblogs.com/zhaotianff/p/17511040.html 使用Windows的计划任务功能可以创建定时任务。 使用schtasks.exe可以对计划任务进行管理&#xff0c;而不需要编写额外代码 这里掌握schtasks /CREATE 的几个核心参数就可以快速创建计划任务 /SC …

论文总结:A Survey on Evaluation of Large Language Models-鲁棒性相关内容

A Survey on Evaluation of Large Language Models 只取了鲁棒性相关的内容 LLMs&#xff1a;《A Survey on Evaluation of Large Language Models大型语言模型评估综述》理解智能本质(具备推理能力)、AI评估的重要性(识别当前算法的局限性设 3.2.1 Robustness鲁棒性&#xf…

Gitlab以及分支管理

一、概述 Git 是一个分布式版本控制系统&#xff0c;用于跟踪文件的变化&#xff0c;尤其是源代码的变化。它由 Linus Torvalds 于 2005 年开发&#xff0c;旨在帮助管理大型软件项目的开发过程。 二、Git 的功能特性 Git 是关注于文件数据整体的变化&#xff0c;直接会将文件…

HTTP模块(二)

HTTP 设置 HTTP 响应报文 HTTP报文常见属性&#xff1a; const http require(http);const server http.createServer((request, response) > {// 设置请求状态码 2xx 4xx 5xxresponse.statusCode 200;// 设置请求描述 了解即可response.statusMessage hello// 指定响…

谷粒商城实战笔记-59-商品服务-API-品牌管理-使用逆向工程的前后端代码

文章目录 一&#xff0c; 使用逆向工程生成的代码二&#xff0c;生成品牌管理菜单三&#xff0c;几个小问题 在本次的技术实践中&#xff0c;我们利用逆向工程的方法成功地为后台管理系统增加了品牌管理功能。这种开发方式不仅能快速地构建起功能模块&#xff0c;还能在一定程度…

uni-app声生命周期

应用的生命周期函数在App.vue页面 onLaunch:当uni-app初始化完成时触发&#xff08;全局触发一次&#xff09; onShow:当uni-app启动&#xff0c;或从后台进入前台时显示 onHide:当uni-app从前台进入后台 onError:当uni-app报错时触发,异常信息为err 页面的生命周期 onLoad…

Linux-安装VMware-01

一、认识linux Linux 是一个开源的类 Unix 操作系统&#xff0c;由林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;于1991年首次发布。Linux 是许多计算机硬件的底层操作系统&#xff0c;特别是服务器、嵌入式系统和个人电脑。它支持多种架构&#xff0c;包括 x86、x64、A…

算法学习笔记(8.8)-多重背包

目录 Question: 思路解析&#xff1a; 代码示例 多重背包的优化问题&#xff1a; 1.二进制优化 代码示例&#xff1a; 2.单调队列优化(滑动窗口) 代码示例 Question: 4. 多重背包问题 I - AcWing题库https://www.acwing.com/problem/content/description/4/ 多重背包简单来说其…

eqmx上读取数据处理以后添加到数据库中

目录 定义一些静态变量 定时器事件的处理器 订阅数据的执行器 处理json格式数据和将处理好的数据添加到数据库中 要求和最终效果 总结一下 定义一些静态变量 // 在这里都定义成全局的 一般都定义成静态的private static MqttClient mqttClient; // mqtt客户端 private s…

springboo 整合 redis

springBoot 整合 redis starter启动依赖。—包含自动装配类—完成相应的装配功能。 引入依赖 <!--引入了redis整合springboot 的依赖--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis&…

基于opencv[python]的人脸检测

1 图片爬虫 这里的代码转载自&#xff1a;http://t.csdnimg.cn/T4R4F # 获取图片数据 import os.path import fake_useragent import requests from lxml import etree# UA伪装 head {"User-Agent": fake_useragent.UserAgent().random}pic_name 0 def request_pic…

谷粒商城实战笔记-65-商品服务-API-品牌管理-表单校验自定义校验器

文章目录 1&#xff0c;el-form品牌logo图片自定义显示2&#xff0c;重新导入和注册element-ui组件3&#xff0c;修改brand-add-or-update.vue控件的表单校验规则firstLetter 校验规则sort 校验规则 1&#xff0c;el-form品牌logo图片自定义显示 为了在品牌列表中自定义显示品…

Typora 以 Github 作为图床使用 PicGo 上传图片

本文简练快速介绍如标题所述的操作流程 文章目录 1.前言1.1 图床简述1.2 Github图床的优缺点1.2.1 优点1.2.2 缺点 2.下载PicGo3.Github访问加速4.用github创建图床服务器4.1 注册4.2 创建仓库 4.3 生成TOKEN令牌5.设置PicGo6.设置Typora7.完成 1.前言 1.1 图床简述 图床&…

人工智能背后的图灵测试(TuringTest)是什么?

人工智能背后的图灵测试(TuringTest)是什么&#xff1f; 一、什么是图灵测试 图灵测试&#xff08;Turing Test&#xff09;由英国数学家和计算机科学家阿兰图灵&#xff08;Alan Turing&#xff09;在1950年提出&#xff0c;用以判断机器是否具有人类智能。图灵在其论文《计…

3.1、数据结构-线性表

数据结构 数据结构线性结构线性表顺序存储和链式存储区别单链表的插入和删除练习题 栈和队列练习题 串&#xff08;了解&#xff09; 数据结构 数据结构该章节非常重要&#xff0c;上午每年都会考10-12分选择题下午一个大题 什么叫数据结构&#xff1f;我们首先来理解一下什…

【Android】碎片—动态添加、创建Fragment生命周期、通信

简单用法 在一个活动中添加两个碎片&#xff0c;并让这两个碎片平分活动空间 先新建一个左侧碎片布局和一个右侧碎片布局 左侧碎片 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/…

Unity Android接入SDK 遇到的问题

1. buildtools、platformtools、commandline tools 以及compiled sdk version、buildtools sdk version、target sdk version 的说明 Android targetSdkVersion了解一下 - 简书 2. 查看.class 和.jar文件 jd_gui 官网地址&#xff1a; 下载jd_gui 工具 &#xff0c;或者 idea 下…

ITK-均值滤波

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 均值滤波原理 均值滤波是一种常用的图像平滑技术&#xff0c;用于减少图像中的噪声。其基本原理是通过计算图像中每个像素及其邻…

linux ftp操作记录

一.ftp 创建用户 passwd: user ftpuser does not exist 如果你遇到 passwd: user ftpuser does not exist 的错误&#xff0c;这意味着系统中不存在名为 ftpuser 的用户。你需要首先确认FTP用户是否是系统用户&#xff0c;还是FTP服务器软件&#xff08;如Pure-FTPd&#xff…

【React】通过实际示例详解评论列表渲染和删除

文章目录 一、引言二、初始状态与状态更新1. 使用useState钩子管理状态2. 评论列表的初始数据 三、列表渲染的实现1. list.map(item > { ... })2. return 语句3. JSX 语法4. 为什么这样设计5. 完整解读 四、列表项的唯一标识1. key 的作用2. key 的用法3. 可以没有 key 吗&a…