图像处理常见的两种拉流方式

news2024/11/15 16:35:12

传统算法或者深度学习在进行图像处理之前,总是会首先进行图像的采集,也就是所谓的拉流。解决拉流的方式有两种,一个是直接使用opencv进行取流,另一个是使用ffmpeg进行取流,如下分别介绍这两种方式进行拉流处理。

1、opencv直接取流

opencv的取流方式主要是利用VideoCapture类进行处理的。VideoCapture提供了一整套的读取视频流信息的方案,主要的函数如下:
VideoCapture有三个构造函数:

  • 不带任何参数的构造函数
    在这里插入图片描述
  • 带有一个视频流地址的构造函数
    在这里插入图片描述
  • 带有一个视频index的构造函数
    在这里插入图片描述
    isOpened()函数主要是判定是否成功打开流地址
    在这里插入图片描述
    read()读取视频数据
    在这里插入图片描述
    release()函数用于释放类对象
    在这里插入图片描述
    具体参考地址:https://docs.opencv.org/4.0.0/d8/dfe/classcv_1_1VideoCapture.html

1.1 python拉流

主要流程为分为以下几步:

  • 通过流的地址实例化VideoCapture类
  • 判断是否成功打开流地址
  • 循环读取每一帧流数据并处理
  • 释放实例化的对象
  • 释放cv
def vedio2Img(vedio_path, save_path):
    cap = cv2.VideoCapture(vedio_path)
    fps = int(cap.get(cv2.CAP_PROP_FPS))
    total_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
    count = 0
    img_idx = 0
    if not cap.isOpened():
        return
    while True:
        success, frame = cap.read()
        if success:
            try:
                count += 1
                if count % fps == 0:
                    img_idx += 1
                    name = save_path.split('\\')[-1]
                    save_path1 = os.path.join(save_path, '{}_vedio_{}.jpg'.format(name, str(img_idx)))
                    save_img(save_path1, frame)
                    print('finish number {} img save'.format(img_idx))
                    cv2.waitKey(1)
            except:
                print('encounter some wrong')
                continue
    cap.release()
    cv2.destroyAllWindows()

1.2 C++ opencv拉流

c++的使用opencv拉流的方式和opencv基本一致(ps:python的底层应该是C++实现的),因此其实现格式如下所示:

	std::string vedio_path = "rtsp://admin:123456@127.0.0.1/Streaming/Channels/11000";
	cv::VideoCapture cap;
	cap.open(vedio_path);
	if (!cap.isOpened()) {
		std::cout << "error about cap" << std::endl;
	}
	VideoFrameDecode videoframe;
	cv::Mat frame;
	while (cap.read(frame))
	{
		if (frame.empty()) {
			break;
		}
		int w = frame.size().width;
		int h = frame.size().height;
		printf("h=%i,w=%i", h, w);
		unsigned char* buffer = frame.data;
		size_t stride = frame.step;
		cv::Mat img = cv::Mat(h, w, CV_8UC3, (void*)buffer, stride);
		cv::namedWindow("demo", cv::WINDOW_NORMAL);
		cv::imshow("demo", img);
		cv::waitKey(0);

	}
	cap.release();
	cv::destroyAllWindows();

2、ffmpeg拉流(C++实现)

  • 下载ffmpeg包的
    ffmpeg包下载地址
    博主下载的5.1.2版本
    在这里插入图片描述

  • vs2022配置使用
    在C/C+±>附加包含目录中添加新下载的ffmpeg包的include路径
    在这里插入图片描述
    在链接器->附加库目录中添加ffmpeg包的lib文件路径
    在这里插入图片描述
    在链接器->输入->附加依赖项中加入所需要的lib库目录,整理如下:

    avcodec.lib
    avdevice.lib
    avfilter.lib
    avformat.lib
    avutil.lib
    swresample.lib
    swscale.lib
    

    如果不想在环境变量中配置ffmpeg中bin文件的目录,可以使用如下方式临时配置:
    在调试->环境中使用Path=D:\ffmpeg\bin;%PATH即可临时使用
    在这里插入图片描述

博主将拉流方式封装为一个类,主要代码如下所示:
ffmpeg.h文件如下:

#ifndef __FFMPEG_DECODE_H__
#define __FFMPEG_DECODE_H__

// Opencv
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
extern "C"
{
#include<libavutil/avutil.h>
#include<libavutil/imgutils.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include<libavdevice/avdevice.h>
};

struct VideoFrameDecode {
	void* buffer;		//֡帧的buffer指针(仅支持RGB格式)
	int pitch;			//图像一行的宽度
};

class ReadFfmpeg
{
public:
	ReadFfmpeg(char* rtsppath);
	~ReadFfmpeg();
	void processOneFrame(cv::Mat &img);

private:
	AVFormatContext* formatContext = nullptr; 
	int ret = -1;
	int videoStreamIndex = -1;
	AVCodecParameters* codecParameters = nullptr;
	const AVCodec* codec = nullptr; 
	AVCodecContext* codecContext = nullptr; 
	AVPacket packet; 

	AVFrame* pFrameRGB;
	uint8_t* buffer;
	SwsContext* sws_ctx; 

};
#endif

其中具体实现的ffmpeg.cpp文件如下所示

#include "ReadFfmpeg.h"
#include <iostream>
#include<chrono>
#include<thread>
using namespace std;

ReadFfmpeg::ReadFfmpeg(char* rtsppath)
{
	avformat_network_init();
	AVDictionary* formatOptions = nullptr;
	av_dict_set_int(&formatOptions, "buffer_size", 2 << 20, 0);
	av_dict_set(&formatOptions, "rtsp_transport", "tcp", 0); //默认使用udp协议进行传输,会出现max delay reached. need to consume packet 
	av_dict_set_int(&formatOptions, "timeout", 5000000, 0);

	formatContext = avformat_alloc_context();
	ret = avformat_open_input(&formatContext, rtsppath, nullptr, &formatOptions);
	if (ret != 0) {
		std::cerr << "Failed to open RTSP stream." << std::endl;
	}
	ret = avformat_find_stream_info(formatContext, nullptr);
	if (ret < 0) {
		std::cerr << "Failed to find stream info." << std::endl;
	}
	
	for (unsigned int i = 0; i < formatContext->nb_streams; ++i) {
		if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			videoStreamIndex = i;
			break;
		}
	}
	if (videoStreamIndex == -1) {
		std::cerr << "Failed to find video stream." << std::endl;
	}
	codecParameters = formatContext->streams[videoStreamIndex]->codecpar;

	codec = avcodec_find_decoder(codecParameters->codec_id);

	if (codec == nullptr) {
		std::cerr << "Failed to find video decoder." << std::endl;
	}
	codecContext = avcodec_alloc_context3(codec);
	if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {
		std::cerr << "Failed to allocate codec context." << std::endl;
	}
	ret = avcodec_open2(codecContext, codec, nullptr);
	if (ret < 0) {
		std::cerr << "Failed to open codec." << std::endl;
	}
	pFrameRGB = av_frame_alloc();

	buffer = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, codecContext->width, codecContext->height, 1));
	av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, codecContext->width, codecContext->height, 1);

	sws_ctx = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt,
		codecContext->width, codecContext->height, AV_PIX_FMT_RGB24,
		SWS_BILINEAR, nullptr, nullptr, nullptr);
	ret = av_read_frame(formatContext, &packet);
	if (ret < 0) {
		std::cerr << "Failed to open packet." << std::endl;
	}
}

ReadFfmpeg::~ReadFfmpeg()
{
	avformat_network_deinit();
	avcodec_free_context(&codecContext);
	sws_freeContext(sws_ctx);	
	av_free(pFrameRGB);
	av_free(buffer);
	av_free(codecParameters);
	avformat_close_input(&formatContext);
}

void ReadFfmpeg::processOneFrame(cv::Mat& img)
{
	if (img.empty())
	{
		img = cv::Mat(codecContext->height, codecContext->width, CV_8UC3);
	}
	int ret = av_read_frame(formatContext, &packet);
	if (ret >= 0) {
		if (packet.stream_index == videoStreamIndex) {
			avcodec_send_packet(codecContext, &packet);
			AVFrame* avFrame = av_frame_alloc();
			int res = avcodec_receive_frame(codecContext, avFrame);
			if (res == 0) {
				// Convert frame to RGB
				sws_scale(sws_ctx, avFrame->data, avFrame->linesize, 0, codecContext->height, pFrameRGB->data, pFrameRGB->linesize);
				img.data = pFrameRGB->data[0];
			}
			av_frame_free(&avFrame);
		}
	}
	av_packet_unref(&packet);
}

void test() {
	char* filename = (char*)"rtsp://admin:123456@127.0.0.1:10000/Streaming/Channels/10000";
	ReadFfmpeg* fmpeg = new ReadFfmpeg(filename);
	cv::Mat img;
	int nFrame = 0;
	auto start = std::chrono::system_clock::now();
	for (;;)
	{
		nFrame++;
		fmpeg->processOneFrame(img);
		if (nFrame % 100==0) {
			nFrame = 0;
			auto end = std::chrono::system_clock::now();
			auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
			std::cout << "the fps is: " << static_cast<float>(100 / (duration.count() / 1000.0)) << std::endl;
			start = end;
		}
		// Display frame
		cv::namedWindow("RTSP Stream", cv::WINDOW_NORMAL);
		cv::imshow("RTSP Stream", img);
		cv::waitKey(1);
	}
	delete fmpeg;

}

以上是一个非常简单的拉流方式,仅可以用作一个demo,实现流的读取,如果想达到实时状态的取流和处理,需要使用多线程的方式,实现一个读取流数据的线程,将数据放入队列,同时实现一个读取流数据的线程,从队列读取数据,同时运行。

附录

实际上opencv也是可以使用ffmpeg的方式进行拉流的,只不过需要在编译opencv的时候,指定ffmpeg版本。

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

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

相关文章

基于深度学习创建-表情符号--附源码

表情符号深度学习概述 如今,我们使用多种表情符号或头像来表达我们的心情或感受。它们充当人类的非语言线索。它们成为情感识别、在线聊天、品牌情感、产品评论等的关键部分。针对表情符号驱动的故事讲述的数据科学研究不断增加。 从图像中检测人类情绪非常流行,这可能是由…

【ROS】参数服务器--理论模型与参数操作(C++)

一、概念介绍 参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据。 作用&#xff1a;存储一些多节点…

Gradio详细文档——快速部署可视化人工智能应用

前言 Gradio是一个开源的Python库&#xff0c;用于快速构建机器学习和数据科学演示的应用。它可以帮助你快速创建一个简单漂亮的用户界面&#xff0c;以便向客户、合作者、用户或学生展示你的机器学习模型。此外&#xff0c;还可以通过自动共享链接快速部署模型&#xff0c;并获…

消息中间件相关面试题

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱发博客的嗯哼&#xff0c;爱好Java的小菜鸟 &#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&#x1f44d;一下博主哦 &#x1f4dd;社区论坛&#xff1a;希望大家能加入社区共同进步…

VALN-hybrid模式

实验拓扑及要求 一、实验思路 1.R1-R3按要求配置&#xff0c;R2不划分vlan使其全部都可以访问 2.交换机和路由器的交换机直连接口设为hybrid模式且R4-R6不带vlan标签访问路由器 3.交换机和交换机的两个直连接口设为hybrid模式且只允许R4-R6所在vlan标签通过 4.R4-R6只允许其…

野火i.mx 6ull上手

目录 屏幕驱动打印信息 实现触摸屏校验 开发板连接WIFI 连接操作 申请路由器动态IP和ping网络通断 WiFi信息保存位置 常用wifi操作&#xff08;wpa_cli工具&#xff09; NFS网络文件系统共享 虚拟机安装NFS服务器 开发板安装NFS客户端 控制开发板 找出硬件设备所对…

Debian10: 安装nut服务器(UPS)

UPS说明&#xff1a; UPS的作用就不必讲了&#xff0c;我选择是SANTAKTGBOX-850&#xff0c;规格为 850VA/510W&#xff0c;可以满足所需&#xff0c;关键是Debian10自带了驱动可以支持&#xff0c;免去安装驱动&#xff0c;将UPS通过USB线连接服务器即可&#xff0c;如下图所示…

wvp-gb28181-pro较新版本的调整说明

gitee地址 wvp-GB28181-pro: WEB VIDEO PLATFORM是一个基于GB28181-2016标准实现的网络视频平台&#xff0c;负责实现核心信令与设备管理后台部分&#xff0c;支持NAT穿透&#xff0c;支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。支持国标级联&#xff0c;支持rtsp/rtmp等…

Git判断本地是否最新

场景需求 需要判断是否有新内容更新,确定有更新之后执行pull操作&#xff0c;然后pull成功之后再将新内容进行复制到其他地方 pgit log -1 --prettyformat:"%H" HEAD -- . "origin/HEAD" rgit rev-parse origin/HEAD if [[ $p $r ]];thenecho "Is La…

【java毕业设计】基于ssm+mysql+jsp的大学生兼职信息系统设计与实现(程序源码)-大学生兼职信息系统

基于ssmmysqljsp的大学生兼职信息系统设计与实现&#xff08;程序源码毕业论文&#xff09; 大家好&#xff0c;今天给大家介绍基于ssmmysqljsp的大学生兼职信息系统设计与实现&#xff0c;本论文只截取部分文章重点&#xff0c;文章末尾附有本毕业设计完整源码及论文的获取方式…

简单理解Linux中的一切皆文件

一款操作系统要管理各种各样不同的硬件&#xff0c;因为硬件的不同所以它们使用的文件系统也不同。但是按道理来说&#xff0c;文件系统的不同对于用户来说可不是一件好事&#xff0c;操作不同的硬件就要使用不同的方法。 但是Linux有一切皆文件。 简单来说&#xff0c;Linux…

React快速入门

最近需要学到react&#xff0c;这里进行一个快速的入门&#xff0c;参考react官网 1.创建和嵌套组件 react的组件封装是个思想&#xff0c;我这里快速演示代码&#xff0c;自己本身也不太熟悉。 代码的路径是src底下的App.js function MyButton() {return (<button>I…

chapter 4 能带理论 energy band

继承自chapter 3 的自由电子模型&#xff1a; 4.1 单电子近似 One electron approximation 列出电子运动的薛定谔方程&#xff1a; E Ψ − ℏ 2 2 m ∇ 2 Ψ U Ψ E \Psi -\frac{\hbar^2}{2m} \nabla^2 \Psi U \Psi EΨ−2mℏ2​∇2ΨUΨ 根据电子在晶体中运动的实际情…

Python编程——列表解析与常用操作

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;Python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注。 目录 一、列表是什么&#xff1f; 二、列表的特点 1、元素…

构建C语言开发环境

有些C/C项目开发周期极长。在处理此类项目过程中&#xff0c;构建开发环境就像施展魔法一样&#xff1a;测试框架被巧妙集成在一起&#xff0c;CI/CD流程将开发者从繁琐重复的工作中解脱出来。 作为程序员&#xff0c;在开发过程中&#xff0c;我只有一个简单的愿望&#xff1…

干翻Dubbo系列第十二篇:Dubbo协议介绍

文章目录 文章说明 一&#xff1a;Dubbo协议 1&#xff1a;Dubbo协议简介 2&#xff1a;Dubbo协议优点 3&#xff1a;Dubbo协议帧的组成 (一)&#xff1a;幻数 (二)&#xff1a;2Way (三)&#xff1a;event (四)&#xff1a;Serilization ID (五)&#xff1a;status …

react之react-redux的介绍、基本使用、获取状态、分发动作、数据流、reducer的分离与合并等

react之react-redux的介绍、基本使用、获取状态、分发动作、数据流、reducer的分离与合并等 一、react-redux介绍二、React-Redux-基本使用三、获取状态useSelector四、分发动作useDispatch五、 Redux 数据流六、代码结构七、ActionType的使用八、Reducer的分离与合并九、购物挣…

python:tkinter + cef 模仿 mdict 界面

cefpython3 其上游是C开发的CEF&#xff08;基于webkit、V8&#xff09;&#xff0c; CEF 即 (Chromium Embedder Framework)&#xff0c; 是基于Google Chromium项目的开源 Web browser控件(WebView)。 可查看github文档&#xff1a;cefpython api pip install cefpython3 c…

信号灯集和共享内存的综合应用小例子

要求&#xff1a;使用信号灯集和共享内存实现&#xff1a;一个进程对共享内存存放数据"Nice to meet you"循环倒置&#xff0c;一个进程循环输出共享内存的内容&#xff0c;要确保倒置一次打印一次。 分析&#xff1a;这两个进程可以写成两个源文件&#xff0c;一个…

回归预测 | MATLAB实现SSA-SVM麻雀搜索算法优化支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现SSA-SVM麻雀搜索算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现SSA-SVM麻雀搜索算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基…