opencv rtsp 硬件解码

news2024/11/18 7:47:02

讨论使用opencv的reader

硬件解码的方案有太多种,如果使用ffmpeg硬件解码是最方便的,不方便的是把解码过后的GPU 拉到 CPU 上,再使用opencv的Mat 从cpu 上上载到gpu上,是不是多了两个过程,应该是直接从GPU mat 直接去处理, 最后一步再从GPU mat 上下载到cpu,render显示。

GPU 硬件解码是nv12 格式,我们为了显示和cpu使用直接转成了RGB或者BGR, 使用opencv再映射封装,最后又上载到cuda,这个过程很耗时间,而且不是必要的。

windows下使用cuda

经过实验,cv::cudacodec::createVideoReader 是可以拉取rtsp 流的,官方编译的可以读取rtsp,但是在文件流上出了问题,而且还有一个bug,就是在显示的时候,必须关闭一次窗口,才能显示后续的帧,而且还有一点,就是注意这个窗口必须是opengl 窗口,而且要打开这个窗口,而且在编译支持cuda的opencv时必须把opengl 勾选上,所以达不到产品化的要求,以下是测试代码:

#include <iostream>

#include "opencv2/opencv_modules.hpp"

#if defined(HAVE_OPENCV_CUDACODEC)

#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/core/opengl.hpp>
#include <opencv2/cudacodec.hpp>
#include <opencv2/highgui.hpp>

#if _DEBUG
#pragma comment(lib,"opencv_world460.lib")
#else 
#pragma comment(lib,"opencv_world460.lib")
#endif
int main()
{
    cv::cuda::printCudaDeviceInfo(cv::cuda::getDevice());
    int count = cv::cuda::getCudaEnabledDeviceCount();
    printf("GPU Device Count : %d \n", count);
    const std::string fname("rtsp://127.0.0.1/101-640.mkv"); //视频文件
   // const std::string fname("test_222.mp4"); //视频文件

   // cv::namedWindow("CPU", cv::WINDOW_NORMAL);
    cv::namedWindow("GPU", cv::WINDOW_OPENGL);
    cv::cuda::setGlDevice();

    cv::Mat frame;
    cv::VideoCapture reader(fname);

    cv::cuda::GpuMat d_frame;
    cv::Ptr<cv::cudacodec::VideoReader> d_reader = cv::cudacodec::createVideoReader(fname);

    cv::TickMeter tm;
    std::vector<double> cpu_times;
    std::vector<double> gpu_times;

    int gpu_frame_count = 0, cpu_frame_count = 0;
#if 0
    for (;;)
    {
        tm.reset(); tm.start();
        if (!reader.read(frame))
            break;
        tm.stop();
        cpu_times.push_back(tm.getTimeMilli());
        cpu_frame_count++;

        cv::imshow("CPU", frame);

        if (cv::waitKey(1) > 0)
            break;
    }
#endif
    for (;;)
    {
        tm.reset();
        tm.start();
        if (!d_reader->nextFrame(d_frame))
            break;
        tm.stop();

        //d_frame.step = d_frame.cols * d_frame.channels();

        //cv::cuda::GpuMat gpuMat_Temp = d_frame.clone();

        gpu_times.push_back(tm.getTimeMilli());
        gpu_frame_count++;
        if (gpu_frame_count > 2)
        {
            cv::Mat test;
            d_frame.download(test);
            d_frame.release();
            // cv::cvtColor(test, test, cv::COLOR_BGRA2BGR);
             //cv::imwrite("./test1.jpg", test);
            cv::imshow("GPU", test);
            
        }
        if (cv::waitKey(1) > 0)
            break;
    }

    if (!cpu_times.empty() && !gpu_times.empty())
    {
        std::cout << std::endl << "Results:" << std::endl;

        std::sort(cpu_times.begin(), cpu_times.end());
        std::sort(gpu_times.begin(), gpu_times.end());

        double cpu_avg = std::accumulate(cpu_times.begin(), cpu_times.end(), 0.0) / cpu_times.size();
        double gpu_avg = std::accumulate(gpu_times.begin(), gpu_times.end(), 0.0) / gpu_times.size();

        std::cout << "CPU : Avg : " << cpu_avg << " ms FPS : " << 1000.0 / cpu_avg << " Frames " << cpu_frame_count << std::endl;
        std::cout << "GPU : Avg : " << gpu_avg << " ms FPS : " << 1000.0 / gpu_avg << " Frames " << gpu_frame_count << std::endl;
    }

    return 0;
}

经过release版本的测试,cuda硬件解码比cpu慢很多,我cpu是intel 13代 13700,速度很快,gpu是3060ti, 实际测试就是如此。
说明在windows下实际类里面解码的时候在cpu和gpu上转换的时间太多
在这里插入图片描述
    综上所述,必须使用更为简单的方法,放弃windows上的做法,放到linux上, ffmpeg硬件解码 然后映射到gpu mat上,至于解码ffmpeg 可以看我的其他文章,至于ffmpeg 编解码 nvidia 上官网也是有介绍的:
编译ffmpeg
    使用python和linux,使用python的作用是取消c++ 到python之间的内存共享,在windows上编译pynvcodec 会遇到各种问题,建议在linux 编译 pynvcodec,为什么不使用ffmpeg直接解码,因为:我们使用ffmpeg解码得到的YUV格式,我们只能在CPU下转化到RGB的色彩空间,缺少在GPU上进行全部转化的流程,因此我们使用vpf 来进行python上的视频处理,同时结束时可以直接转化成pytorch的张量来处理。

    VideoProcessingFramework(VPF)是NVIDIA开源的适用于Python的视频处理框架,可用于硬件加速条件下的视频编解码等处理类任务。同时对于Pytorch比较友好,能够将解析出来的图像数据直接转化成Tensor()的格式。以下为例子:

import PyNvCodec as nvc
import PytorchNvCodec as pnvc  
  
   while True:
        # Read data.
        # Amount doesn't really matter, will be updated later on during decode.
        bits = proc.stdout.read(read_size)
        if not len(bits):
            print("Can't read data from pipe")
            break
        else:
            rt += len(bits)

        # Decode
        enc_packet = np.frombuffer(buffer=bits, dtype=np.uint8)
        pkt_data = nvc.PacketData()
        try:
            surf = nvdec.DecodeSurfaceFromPacket(enc_packet, pkt_data)    # 获取流的数据

            # Convert to planar RGB
            rgb_pln = to_rgb.run(surf)   # 转换到rgb_pln
            if rgb_pln.Empty():
                break

            # PROCESS YOUR TENSOR HERE.
            # THIS DUMMY PROCESSING JUST ADDS RANDOM ROTATION.
            src_tensor = surface_to_tensor(rgb_pln)  # 转化为Tensor(),数据存储在GPU中
            dst_tensor = T.RandomRotation(degrees=(-1, 1))(src_tensor)
            surface_rgb = tensor_to_surface(dst_tensor, gpu_id)

            # Convert back to NV12
            dst_surface = to_nv12.run(surface_rgb) # 再转换回码流
            if src_surface.Empty():
                break

        # Handle HW exceptions in simplest possible way by decoder respawn
        except nvc.HwResetException:
            nvdec = nvc.PyNvDecoder(w, h, f, c, g)
            continue

使用gstreamer

近来来opencv的下载是一个问题,动不动就下载出错,使用gstreamer 在windows下和ffmpeg 差不离,编译也比较麻烦,我们尽量在linux下编译

sudo apt-get update 
sudo apt-get install build-essential cmake git pkg-config 
sudo apt-get install libjpeg8-dev libtiff4-dev libjasper-dev libpng12-dev 
sudo apt-get install libgtk2.0-dev 
sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev 
sudo apt-get install libatlas-base-dev gfortran 
//在opencv里面安装gstreamer插件 
sudo apt-get install gstreamer1.0-tools gstreamer1.0-alsa gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav 
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1.0-dev libgstreamer-plugins-bad1.0-dev 

cd /home/opencv 
git clone https://github.com/opencv.git 
cd opencv 
git checkout 4.7.0 
cd /home/opcv 
nkdir build 
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D CUDA_GENERATION=Kepler .. 
make -j4 
sudo make install
int main()
{
   // std::cout << cv::getBuildInformation() << std::endl;

    using std::chrono::steady_clock;
    typedef std::chrono::milliseconds milliseconds_type;
    const int interval = 15;

    std::stringstream ss;
    std::string rtsp_url = "rtsp://127.0.0.1/101-640.mkv";
    size_t latency = 200;
    size_t frame_width = 1920;
    size_t frame_height = 1080;
    size_t framerate = 15;

    ss << "rtspsrc location=" << rtsp_url << " latency=" << latency << " ! application/x-rtp, media=video, encoding-name=H264 "
        << "! rtph264depay ! video/x-h264, clock-rate=90000, width=" << frame_width << ", height=" << frame_height << ", framerate="
        << framerate << "/1 ! nvv4l2decoder ! video/x-raw(memory:NVMM), width=" << frame_width << ", height=" << frame_height
        << ", framerate=" << framerate << "/1 ! nvvideoconvert ! video/x-raw, format=BGRx ! videoconvert ! video/x-raw, format=BGR ! appsink";
    std::cout << ss.str() << std::endl;

    cv::VideoCapture cap = cv::VideoCapture(ss.str(), cv::CAP_GSTREAMER);
    if (!cap.isOpened())
    {
        std::cerr << "error to open camera." << std::endl;
        return -1;
    }
    std::cout << cv::getBuildInformation() << std::endl;
    cv::Mat frame;
    steady_clock::time_point start = steady_clock::now();
    size_t frame_idx = 0;

    while (1)
    {
        bool ret = cap.read(frame);
        if (ret)
        {
            // cv::imwrite("tmp.jpg", frame);
            ++frame_idx;
        }
        if (frame_idx % interval == 0)
        {
            steady_clock::time_point end = steady_clock::now();
            milliseconds_type span = std::chrono::duration_cast<milliseconds_type>(end - start);
            std::cout << "it took " << span.count() / frame_idx << " millisencods." << std::endl;
            start = end;
        }
    }
    return 0;
}

一点一点排除,在windows上很难复现很多代码,很多都是不稳当的做法,只能做做demo,完全产品化不了,我们目前稳定的做法,1 是使用live555 ,下拉 rtsp,ffmpeg 硬件解码,转成mat,转成gpumat,再转成mat。这个方案不断修改吧。等我更新。

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

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

相关文章

从Bean的生命周期分析Dubbo的源码

写作目的 Dubbo作为RPC中的经典落地实践&#xff0c;作为阿里内部目前还是大规模使用的基础框架&#xff0c;作为CRUD的底层。无论从什么角度来看简单的阅读一下Dubbo的源码还是有必要的。 前提&#xff1a;要了解Bean的生命周期 源码下载 gitee源码下载 源码分析 开启Dub…

《MySQL 实战 45 讲》课程学习笔记(四)

深入浅出索引 索引的出现其实就是为了提高数据查询的效率&#xff0c;就像书的目录一样。 索引的常见模型 哈希表 哈希表是一种以键 - 值&#xff08;key-value&#xff09;存储数据的结构&#xff0c;我们只要输入待查找的值即 key&#xff0c;就可以找到其对应的值即 Val…

时序分析:曲线分解

以下仅为博主个人观点&#xff0c;如有错误欢迎批评指正。 前言后记都挺重要建议还是看一下吧。 文章目录 前言经验模态分解EMDEEMDCEEMDAN 变分模态分解VMD 奇异谱分析SSA 后记 前言 本篇文章将会介绍常用曲线分解方法(经验模态分解及其变种&#xff0c;变分模态分解&#x…

集团企业网站建设开发

为集团提供一个互联网上的形象宣传和信息发布、收集的重要平台 利用最新的互联网动态数据库交互能力&#xff0c;建立一套在互联网上具有领先地位的集团网站&#xff0c;将集团和子公司网站做到有机的统一。集团网站不但要把集团的企业、产品等相关信息展示给我们的客户、合作…

RabbitMQ 教程 | 第2章 RabbitMQ 入门

&#x1f468;&#x1f3fb;‍&#x1f4bb; 热爱摄影的程序员 &#x1f468;&#x1f3fb;‍&#x1f3a8; 喜欢编码的设计师 &#x1f9d5;&#x1f3fb; 擅长设计的剪辑师 &#x1f9d1;&#x1f3fb;‍&#x1f3eb; 一位高冷无情的编码爱好者 大家好&#xff0c;我是 DevO…

1,复杂度和简单排序算法【p2-p3】

复杂度和简单排序算法 1&#xff0c;时间复杂度1.1选择排序1.2冒泡排序1.3异或运算1.3.1性质&#xff1a;1.3.2案例例1例2 1.4插入排序1.5二分法1.5.1在一个有序数组中&#xff0c;找某个数是否存在1.5.2在一个有序数组中&#xff0c;找>某个数最左侧的位置1.5.3局部最小值问…

linux系统编程重点复习--守护进程和线程

复习目标 说出守护进程的特点独立完成守护进程的创建独立实现多个线程的创建独立实现线程的退出和资源回收理解线程同步的思想 1 守护进程 1.1 守护进程介绍 Daemon(精灵)进程&#xff0c;是Linux中的后台服务进程&#xff0c;通常独立于控制终端并且周期性地执行某种任务或…

通向架构师的道路之Apache整合Tomcat

一、先从J2EE工程的通用架构说起 这是一个通用的Web即B/S工程的架构&#xff0c;它由&#xff1a; Web Server App Server DB Server 三大部分组成&#xff0c;其中&#xff1a; Web Server 置于企业防火墙外&#xff0c;这个防火墙&#xff0c;大家可以认为是…

hugging face下载数据集

开始直接执行这个&#xff0c;下载下来的图片打不开 git clone https://huggingface.co/datasets/diffusers/dog-example 解决办法&#xff1a; 安装git lfs 1. curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash 2. sudo apt…

论文笔记:Adjusting for Autocorrelated Errors in Neural Networks for Time Series

2021 NIPS 原来的时间序列预测任务是根据预测论文提出用一阶自回归误差预测 一阶差分&#xff0c;类似于ResNet的残差思路&#xff1f;记为pred&#xff0c;最终的预测结果

zore-shot,迁移学习和多模态学习

1.zore-shot 定义&#xff1a;在ZSL中&#xff0c;某一类别在训练样本中未出现&#xff0c;但是我们知道这个类别的特征&#xff0c;然后通过语料知识库&#xff0c;便可以将这个类别识别出来。概括来说&#xff0c;就是已知描述&#xff0c;对未知类别&#xff08;未在训练集中…

Qsys介绍

文章目录 前言一、为什么需要Qsys1、简化了系统的设计流程2、Qsys涉及的技术 二、Qsys真身1、一种系统集成工具2、何为Nios II1、内核架构2、Nios II选型 三、Qsys设计涉及到的软件&工具四、总结五、参考资料 前言 Qsys是Altera下的一个系统集成工具&#xff0c;可用于搭建…

代码随想录算法训练营第三十天 | 单调栈系列复习

单调栈系列复习 每日温度未看解答自己编写的青春版重点题解的代码日后再次复习重新写 下一个更大元素 I未看解答自己编写的青春版重点题解的代码日后再次复习重新写 下一个更大元素II未看解答自己编写的青春版重点题解的代码日后再次复习重新写 接雨水未看解答自己编写的青春版…

idea模块的pom.xml被划横线,不识别的解决办法

目录 问题&#xff1a; 解决办法&#xff1a; 1.打开设置 2. 取消勾选 3.点击确认 4.解决 问题提出&#xff1a; 写shi山的过程中&#xff0c;给模块取错名字了&#xff0c;改名的时候不知道点到了什么&#xff0c;一个模块的pom.xml变成灰色了&#xff0…

3ds MAX粒子系统演示

其他各案例可以在视频中看到&#xff1a; 从左向右依次是&#xff1a; 粒子流源、喷射、雪、超级喷射、暴风雪、粒子阵列、粒子云 粒子系统

小研究 - JVM 垃圾回收方式性能研究(一)

本文从几种JVM垃圾回收方式及原理出发&#xff0c;研究了在 SPEC jbb2015基准测试中不同垃圾回收方式对于JVM 性能的影响&#xff0c;并通过最终测试数据对比&#xff0c;给出了不同应用场景下如何选择垃圾回收策略的方法。 目录 1 引言 2 垃圾回收算法 2.1 标记清除法 2.2…

iOS开发-实现获取下载主题配置动态切换主题

iOS开发-实现获取下载主题配置动态切换主题 iOS开发-实现获取下载主题配置更切换主题&#xff0c;主要是通过请求服务端配置的主题配置、下载主题、解压保存到本地。通知界面获取对应的图片及颜色等。 比如新年主题风格&#xff0c;常见的背景显示红色氛围图片、tabbar显示新…

Visual Studio 设置默认编码格式为 UTF-8

1.添加高级保存选项 2.更改编码格式

客户端服务器通过Socket API通信流程 通过源码角度分析 三握手四挥手都过程的状态改变 及 例如accept()connect()具体做了什么

首先我们先说下网络编程API&#xff1a; 数据在网络上通信&#xff0c;通信的双方一个是 客户端&#xff0c; 一个是 服务器 更具体来说&#xff0c;不是 客户端和服务器这两个机器在 经由互联网 进行通信&#xff0c; 而是 客户端上的某一进程 与 服务器端的某一进程 进…

Java 单链表

链表基本介绍 链表在内存中的实际存储结构 链表的逻辑结构 单链表应用实例 代码实现 // 英雄节点&#xff0c;存储了英雄的信息 class HeroNode {public int id; // 英雄编号public String name; // 英雄名字public String nickName; // 英雄昵称public HeroNode next; // 指…