[RDK X5] MJPG编解码开发实战:从官方API到OpenWanderary库的C++/Python实现

news2025/6/9 10:51:34

业余时间一直在基于RDK X5搞一些小研究,需要基于高分辨率图像检测目标。实际落地时,在图像采集上遇到了个大坑。首先,考虑到可行性,我挑选了一个性价比最高的百元内摄像头,已确定可以在X5上使用,接下来就开始一系列的痛苦适配流程😭。

检测算法能够应用的前提是摄像头采集与实际时间不能相差过多,如果使用下面这种通用的采集方式的话,延迟在3-7s,且每秒最多显示1帧

import cv2
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 3264)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 2448)

cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
while True:
    ret, img = self.cap.read()
    if not ret:
      print("Failed to capture image from camera")
      return
    img = cv2.resize(img , (3264 // 4, 2448 //4))
    cv2.imshow('frame', img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()

如果是这样的话,那就别想着算法落地了,完全没法用。利用了我5个月的闲暇时间(唉,工作巨忙,抽出时间真的太难了),终于把问题和对应解决方案都给摸了,最终结论如下:800M分辨率下,延迟200ms,FPS在20-30之间,已经达到可用的标准。

本文将带给各位如下内容:

  • 编解码问题剖析
  • 官方PyAPI的编解码示例
  • OpenWanderary编解码示例(个人维护库)

一 采集问题剖析

  • 为什么每秒只能采集1帧? cap = cv2.VideoCapture(0)的默认采集模式为CAP_ANY,用VLC工具(sudo apt-get install vlc)打开摄像头查看默认配置。发现默认是YUV模式,根据摄像头文档,YUV模式就是每秒只能播一帧。
  • 如果只是采集1帧的话,为什么采集延迟在3-7s? 参考下面代码获取CAP_PROP_BUFFERSIZE的参数,返回值为4。这个参数表示会自动存储最新的4帧图像,如果我们无法及时在1s内处理完数据,很容易读取到缓存图像,即4s前的数据。

参考上述分析出的问题,我们可以将采集代码改为如下形式,这样我们就可以获得稍微快速的效果

import cv2
# 切换为V4L2模式,该模式下将利用linux的v4l2工具完成图像采集
cap = cv2.VideoCapture(0, cv2.CAP_V4L2)  
# 切换为MJPG模式采集图像,该模式下摄像头FPS为30FPS。
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 3264)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 2448)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # 保留1帧缓存,不需要读取旧图像

# 后面采集方式跟之前一样
cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
while True:
    ret, img = self.cap.read()
    if not ret:
      print("Failed to capture image from camera")
      return
    img = cv2.resize(img , (3264 // 4, 2448 //4))
    cv2.imshow('frame', img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()

优化之后,每秒大概可处理3-5帧左右图像,延迟在0.5-1s之间。

二 利用官方Py接口实现硬件编解码

硬件编解码指利用X5的专用编解码芯片来处理图像的编解码操作。相比纯软件实现,硬件编解码能显著提高处理速度。为了搞通编解码,我还定位出官方Py包的一个Bug(快说谢谢小玺玺😋)。

2.1 解码MJPG图像

如果需要使用硬件编解码,就必须得想办法暴露出原始的MJPG流,通过翻OpenCV源码,只有将convert_rgb这个变量置为false,才能返回mjpg数据,那么问题简单了,调用cap.set(cv2.CAP_PROP_CONVERT_RGB, 0)即可完成设置。

在这里插入图片描述
官方的解码库导入方式为from hobot_vio.libsrcampy import Decoder,具体用法如下:

  • 初始化编码类:dec = Decoder()
  • 定义解码器参数:ret = dec.decode("", 0, 3, imgw, imgh)
    • 第一个参数为file:表示解码数据来源,这里是通过视频流传入解码数据,因此这里指定为""即可。
    • 第二个参数为video_chn:官方说指定解码器的通道号,但从源码来看,这个参数完全没用到,填什么都可以。
    • 第三个参数为decode_type:1表示H264,2表示H265,3表示MJPEG,这里摄像头是MJPG流,因此这里填3
    • 第四/五个参数就是图像的Size。

在这里插入图片描述

  • 向编码器中传入编码数据:dec.set_img(byte_data)
  • 获取解码数据:decode_img_x5 = dec.get_img()

更详细的代码用法如下

import cv2
from hobot_vio import libsrcampy
import numpy as np

cap = cv2.VideoCapture(0, cv2.CAP_V4L2)  
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
imgh, imgw = 2448, 3264
cap.set(cv2.CAP_PROP_FRAME_WIDTH, imgw)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, imgh)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) 
# 返回MJPG数据
cap.set(cv2.CAP_PROP_CONVERT_RGB, 0)

# 设置RDKX5的解码器
dec = libsrcampy.Decoder()
ret = dec.decode("", 0, 3, imgw, imgh)

cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
while True:
    data = cap.grab()
    if not data:
      continue
    # 这里frame返回的是MJPG数据
    ret, frame = cap.retrieve()
    if not ret:
      continue
    
    # 将编码数据传入解码器
    byte_data = frame.tobytes()
    dec.set_img(byte_data)
    decode_img_x5 = dec.get_img()
    decode_img_x5 = cv2.cvtColor(
        np.frombuffer(decode_img_x5, dtype=np.uint8).reshape(imgh * 3 // 2, imgw), 
        cv2.COLOR_YUV2BGR_NV12)
    
    decode_img = cv2.resize(decode_img_x5, (imgw// 4, imgh//4))
    cv2.imshow('frame', decode_img)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()

软硬编解码测试结果如下,硬解码CPU占用多了1倍,但效率优化了4倍左右。

  • OpenCV软解码:平均解码耗时在140ms左右CPU占用为134%
  • RDKX3硬解码:平均解码耗时在34ms左右CPU占用为246%
    在这里插入图片描述

2.2 编码图像为MJPG数据

这里介绍下有编码需求的场景,摄像头采集完图像,利用BPU推理之后,一般会将检测结果绘制到图像上,部分开发这需要利用Flask(Python 编写的轻量级 Web 应用框架)将图像传到网页端进行查看,这时候对原图进行编码传输会提高传输效率,提高可视化的丝滑度。

这里直接给出对应的编码示例,软解码调用data = cv2.imencode(".jpg", img),测试图像分辨率为1280x720。具体测试结论如下:

  • 软编码:耗时18ms,编码后字节数123662
  • 硬编码:耗时3ms,编码后字节数53496
imgpath = "zidane.jpg"
img = cv2.imread(imgpath)
imgh, imgw, chl = img.shape

# 设置编码,参数意义同Decoder
enc = libsrcampy.Encoder()
ret = enc.encode(0, 3, imgw, imgh)
nv12 = bgr2nv12_opencv(img) # 编码输入要求为nv12格式
enc.encode_file(nv12.tobytes()) # 传入需编码的数据
enc_data = enc.get_img() # 返回编码后的字节
buf = np.frombuffer(enc_data, dtype=np.uint8)
dec_img = cv2.imdecode(buf, cv2.IMREAD_COLOR)
print(dec_img.shape)
enc.close()

三 利用OpenWanderary实现Py和C++的编解码

OpenWanderary在23年就已经开始启动了,最开始是在[BPU部署教程] 万字长文!通透解读模型部署端到端大流程中初步介绍了一些功能。主要是针对X3/X5的一些功能进行二次封装,并补充一些轮子库(工作真的是越来越忙,能抽出时间搞这种东西真的就是纯热爱了 💞 )。仓库地址:https://github.com/Li-Zhaoxi/OpenWanderary

为什么我要重新封装,从官方开源的代码和文档里来看,不同的开发板对应的文档是不同的。甚至代码都是完全复制一份来适配的,那么后续有更新,或者有bug修复,真的很不适合持续的发版与迭代。自己开发时候遇到很多问题,比如这个摄像头问题,定位了好久。我个人不喜欢走回头路,比如一个编解码问题只是个接口,内部应该适配多个开发板。不需要用户去区分这些。

在这里插入图片描述
X5的开发板,底层是有个hbm的C语言接口,这个接口是稳定的。无论是官方SDK还是我维护的OpenWanderary,都是基于这个底层的API派生而来。

然后说说自己设计OpenWanderary的几个优点吧:

  • 每个关键函数都有UnitTest测试,而且是数值一致性级别的。
    -
  • 代码规范符合Google开发规范,并有pre-commit功能完成基本的规范性校验。
  • Py接口利用PyBind对现有的C++进行封装,也同时有相关的UnitTest完成测试。

OpenWanderary目前处于持续开发中,迭代过程中部分函数接口会有一些变化,但会始终保持UnitTest的通过

对应的编解码Py版本和C++版本的测试示例如下,MediaCodecJpg类负责MJPG的编解码;

  • 构造阶段:可指定编码或解码
  • 初始化以及释放都是init()close()函数,无序其他参数
  • 无论是编码还是解码主函数都是process。编码时输入的是nv12数据,解码则是正常的1xN字节Mat。

在这里插入图片描述
在这里插入图片描述

四 小结

落地和出Demo完全是一个天一个地。出个Demo,去年就已经搞定了,但是如何将算法落地,达到丝滑的体验,需要更多的时间去打磨。OpenWanderary目前仍然处于初级阶段,我会慢慢将我的所有轮子都补充上去。

编解码问题我目前用着很稳定,各位可以放心食用。后面我也会慢慢完善BPU的接口适配,集成超神Cauchy_WuChao的顶级Yolo项目。

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

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

相关文章

aardio 简单网页自动化

WebView自动化,以前每次重复做网页登录、搜索这些操作时都觉得好麻烦,现在终于能让程序替我干活了,赶紧记录下这个超实用的技能! 一、初次接触WebView WebView自动化就像给程序装了个"网页浏览器",第一步得…

打卡第39天:Dataset 和 Dataloader类

知识点回顾: 1.Dataset类的__getitem__和__len__方法(本质是python的特殊方法) 2.Dataloader类 3.minist手写数据集的了解 作业:了解下cifar数据集,尝试获取其中一张图片 import torch import torch.nn as nn import…

如何做好一份优秀的技术文档:专业指南与最佳实践

如何做好一份优秀的技术文档:专业指南与最佳实践 技术文档是产品开发、用户支持和团队协作的核心工具。高质量的技术文档能够提升开发效率、降低维护成本并改善用户体验。本文将从实践出发,详细讲解如何编写专业、清晰且实用的技术文档。 🌟…

TCP相关问题 第一篇

TCP相关问题1 1.TCP主动断开连接方为什么需要等待2MSL 如上图所示:在被动链接方调用close,发送FIN时进入LAST_ACK状态,但未收到主动连接方的ack确认,需要被动连接方重新发送一个FIN,而为什么是2MSL,一般认为丢失ack在…

6.Pandas 数据可视化图-1

第三章 数据可视化 文章目录 目录 第三章 数据可视化 文章目录 前言 一、数据可视化 二、使用步骤 1.pyplot 1.1引入库 1.2 设置汉字字体 1.3 数据准备 1.4 设置索引列 ​编辑 1.5 调用绘图函数 2.使用seaborn绘图 2.1 安装导入seaborn 2.2 设置背景风格 2.3 调用绘图方法 2.…

软件功能测试报告都包含哪些内容?

软件功能测试报告是软件开发生命周期中的重要文档,主要涵盖以下关键内容:    1.测试概况:概述测试目标、范围和方法,确保读者对测试背景有清晰了解。 2.测试环境:详细描述测试所用的硬件、软件环境,确保…

在Vue或React项目中使用Tailwind CSS实现暗黑模式切换:从系统适配到手动控制

在现代Web开发中,暗黑模式(Dark Mode)已成为提升用户体验的重要功能。本文将带你使用Tailwind CSS在React项目(Vue项目类似)中实现两种暗黑模式控制方式: 系统自动适配 - 根据用户设备偏好自动切换手动切换 - 通过按钮让用户自由选择 一、项目准备 使…

Linux--命令行参数和环境变量

1.命令行参数 Linux 命令行参数基础 1.1参数格式 位置参数:无符号,按顺序传递(如 ls /home/user 中 /home/user 是位置参数) 选项参数: 短选项:以 - 开头,单个字母(如 -l 表示长格…

springboot线上教学平台

摘要:在社会快速发展的影响下,使线上教学平台的管理和运营比过去十年更加理性化。依照这一现实为基础,设计一个快捷而又方便的网上线上教学平台系统是一项十分重要并且有价值的事情。对于传统的线上教学平台控制模型来说,网上线上…

mariadb5.5.56在centos7.6环境安装

mariadb5.5.56在centos7.6环境安装 1 下载安装包 https://mariadb.org/mariadb/all-releases/#5-5 2 上传安装包的服务器 mariadb-5.5.56-linux-systemd-x86_64.tar.gz 3 解压安装包 tar -zxvf mariadb-5.5.56-linux-systemd-x86_64.tar.gz mv mariadb-5.5.56-linux-syst…

打造你的 Android 图像编辑器:深入解析 PhotoEditor 开源库

📸 什么是 PhotoEditor? PhotoEditor 是一个专为 Android 平台设计的开源图像编辑库,旨在为开发者提供简单易用的图像编辑功能。它支持绘图、添加文本、应用滤镜、插入表情符号和贴纸等功能,类似于 Instagram 的编辑体验。该库采…

Azure 虚拟机端口资源:专用 IP 和公共 IP Azure Machine Learning 计算实例BUG

## 报错无解 找不到Azure ML 计算实例关联的 NSG .env 文件和 ufw status: .env 文件中 EXPOSE_NGINX_PORT8080 是正确的,它告诉 docker-compose.yaml 将 Nginx 暴露在宿主机的 8080 端口。 sudo ufw status 显示 Status: inactive,意味着宿…

Java核心技术-卷I-读书笔记(第十二版)

第一章 Java程序设计概述 09年sun被oracle收购->11年java7(简单改进)->14年java8(函数式编程)->2017年java9->2018年java11->2021年java17 第二章 Java编程环境 Java9后新增JShell,提供类似脚本试执…

MATLAB遍历生成20到1000个节点的无线通信网络拓扑推理数据

功能: 遍历生成20到1000个节点的无线通信网络拓扑推理数据,包括网络拓扑和每个节点发射的电磁信号,采样率1MHz/3000,信号时长5.7s,单帧数据波形为实采 数据生成效果: 拓扑及空间位置: 节点电磁…

卫星接收天线G/T值怎么计算?附G/T计算excel表格链接

我们在进行无线通信链路设计时,都会涉及接收天线最重要的参数G/T。今天,咱们就来聊聊G/T值该怎么计算,计算过程中有哪些需要留意的地方,以及当你看到产品说明书中标注了G/T指标,还需要进一步了解哪些信息。 G/T的含义 …

基于dify的营养分析工作流:3分钟生成个人营养分析报告

你去医院做体检,需要多久拿到体检报告呢?医院会为每位病人做一份多维度的健康报告吗?"人工报告需1小时/份?数据误差率高达35%?传统工具无法个性化? Dify工作流AI模型的组合拳,正在重塑健康…

新成果:GaN基VCSEL动态物理模型开发

作为高速数据传输与光电信号处理的核心器件,垂直腔面发射激光器(VCSEL)在高速光通信、激光雷达等领域应用广泛,其动态特性直接关联器件调制速率及稳定性等关键参数。近期,天津赛米卡尔科技有限公司技术团队开发了GaN基…

Appium+python自动化(十一)- 元素定位- 下

1、 List定位 List顾名思义就是一个列表,在python里面也有list这一个说法,如果你不是很理解什么是list,这里暂且理解为一个数组或者说一个集合。首先一个list是一个集合,那么他的个数也就成了不确定性,所以这里需要用复…

免费批量PDF转Word工具

免费批量PDF转Word工具 工具简介 这是一款简单易用的批量PDF转Word工具,支持: 批量转换多个PDF文件保留原始格式和布局快速高效的转换速度完全免费使用 工具地址 下载链接 网盘下载地址:点击下载 提取码:8888 功能特点 ✅…

Mac/iOS 如何解压 RAR 格式压缩包:常用工具与详细操作步骤

一、Mac 系统解压 RAR 文件之法 Mac 系统上解压 RAR 文件有多种方法,除了系统自带的一些简单功能外,还可以借助特定的软件来实现高效解压。以下将介绍几款常用工具的解压操作。 (一)解压专家解压步骤 解压专家 是一款在 Mac 和 …