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

传统算法或者深度学习在进行图像处理之前,总是会首先进行图像的采集,也就是所谓的拉流。解决拉流的方式有两种,一个是直接使用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版本。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mybatis入门的环境搭建及快速完成CRUD(增删改查)

又是爱代码的一天 一、MyBatis的介绍 ( 1 ) 背景 MyBatis 的背景可以追溯到 2002 年&#xff0c;当时 Clinton Begin 开发了一个名为 iBATIS 的持久化框架。iBATIS 的目标是简化 JDBC 编程&#xff0c;提供一种更直观、易用的方式来处理数据库操作。 在传统的 JDBC 编程中&…

DevOps系列文章之 GitlabCICD自动化部署SpringBoot项目

一、概述 本文主要记录如何通过Gitlab CI/CD自动部署SpringBoot项目jar包。 二、前期准备 准备三台 CentOS7服务器&#xff0c;分别部署以下服务&#xff1a; 序号系统IP服务1CentOS7192.168.56.10Gitlab2CentOS7192.168.56.11Runner &#xff08;安装Docker&#xff09;3Cen…

对前端PWA应用的部分理解和基础Demo

一、什么是PWA应用&#xff1f; 1、PWA简介 ​ 渐进式Web应用&#xff08;Progressive Web App&#xff09;&#xff0c;简称PWA&#xff0c;是 Google 在 2015 年提出的一种使用web平台技术构建的应用程序&#xff0c;官方认为其核心在于Reliable&#xff08;可靠的&#xf…

华为ENSP网络设备配置实战4(OSPF+BGP+VPN+单臂路由)

题目要求 1、loopback口通过OSPF连通&#xff0c;合理规划OSPF开销&#xff0c;通过设置AR1->AR2->AR4链路&#xff0c;来消除负载链路。 2、AR3、AR4分别与AR1、AR2建立BGP邻居 3、AR3、AR4作为PC机网关设备 4、PC1、PC3由VPN-spi承载&#xff0c;PC2、PC4由VPN-spims承…

物联网智慧安防实训综合实训基地建设方案

一、系统概述 物联网智慧安防实训综合实训基地是一个为学生提供综合实践、培养技能的场所&#xff0c;专注于物联网技术与智慧安防应用的培训和实训。通过物联网智慧安防实训综合实训基地的建设和运营&#xff0c;学生可以在真实的环境中进行实践训练&#xff0c;提高其物联网技…

【高频面试题】 消息中间件

文章目录 1、RabbitMQ1.1 RabbitMQ-如何保证消息不丢失1.2 RabbitMQ消息的重复消费问题如何解决的1.3 RabbitMQ中死信交换机 ? (RabbitMQ延迟队列有了解过嘛)1.4 RabbitMQ如果有100万消息堆积在MQ , 如何解决(消息堆积怎么解决)1.5 RabbitMQ的高可用机制有了解过嘛 2、Kafka2.…

LlamaGPT -基于Llama 2的自托管类chatgpt聊天机器人

LlamaGPT一个自托管、离线、类似 ChatGPT 的聊天机器人&#xff0c;由 Llama 2 提供支持。100% 私密&#xff0c;不会有任何数据离开你的设备。 推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 1、如何安装LlamaGPT LlamaGPT可以安装在任何x86或arm64系统上。 首先确保…

【微服务】一文了解 Nacos

一文了解 Nacos Nacos 在阿里巴巴起源于 2008 2008 2008 年五彩石项目&#xff08;完成微服务拆分和业务中台建设&#xff09;&#xff0c;成长于十年双十一的洪峰考验&#xff0c;沉淀了简单易用、稳定可靠、性能卓越的核心竞争力。 随着云计算兴起&#xff0c; 2018 2018 20…

C++11并发与多线程笔记(3)线程传参详解,detach()大坑,成员函数做线程函数

C11并发与多线程笔记&#xff08;3&#xff09;线程传参详解&#xff0c;detach 大坑&#xff0c;成员函数做线程函数 1、传递临时对象作为线程参数1.1 要避免的陷阱11.2 要避免的陷阱21.3 总结 2、临时对象作为线程参数2.1 线程id概念2.2 临时对象构造时机抓捕 3、传递类对象…

vscode 安装勾选项解释

1、通过code 打开“操作添加到windows资源管理器文件上下文菜单 &#xff1a;把这个两个勾选上&#xff0c;可以对文件使用鼠标右键&#xff0c;选择VSCode 打开。 2、将code注册为受支持的文件类型的编辑器&#xff1a;不建议勾选&#xff0c;这样会默认使用VSCode打开支持的相…

虫情测报灯

在农业生产过程中&#xff0c;农作物的虫害问题永远都是放在首位的。随着现代生活科技的发展和社会进步&#xff0c;人们对物质也有了新的要求。伴随农作物品种的增加&#xff0c;农药和化肥的使用也在导致农业虫害问题日益加剧&#xff0c;在这种不良的耕作状态下&#xff0c;…

总结 TCP 协议的相关特性

TCP协议段格式: 如图, 端口号: 是其中一个重要的部分,知道端口号才能确认数据交给哪个应用程序(端口号属于传输层的概念). 4位首部长度:4bit表示的范围是0->15,在此处,单位是"4字节",因此,将这里的数值 * 4&#xff0c;才是真正的报头长度,即TCP 报头最大长度,60…

听GPT 讲Prometheus源代码--util

Prometheus的util目录包含了一些通用的工具模块,主要包含以下文件: buckets.go 这个文件定义了一些常用的指标采样值范围(Quantile buckets),如:0.001,0.01,0.05,0.5,0.9,0.95,0.99,0.999等。这些buckets常用于计算指标的分位数线。 regex.go 这个文件定义了一些正则表达式匹配…

Spring事务和事务传播机制(1)

前言&#x1f36d; ❤️❤️❤️SSM专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Spring Spring MVC MyBatis_冷兮雪的博客-CSDN博客 在Spring框架中&#xff0c;事务管理是一种用于维护数据库操作的一致性和…

使用nrm快速切换npm源以及解决Method Not Implemented

文章目录 什么是nrm如何使用nrm查看本机目前使用的npm 源安装nrm查看可选源查看当前使用源切换源添加源删除源测试源的响应时间 如果你遇到这个报错&#xff0c;就可以采用这种方案解决哦解决方案&#xff1a;1. 切换为官方源2. 查看漏洞3. 修复漏洞4. 下面命令慎重使用&#x…

Leetcode 0814周总结

本周刷题&#xff1a; 88, 108, 121, 219, 228, 268, 283, 303, 349, 350, 414, 448 88 合并两个有序数组 nums1{1, 2, 3 ,0, 0, 0} nums2{2, 5, 6} 合成效果&#xff1a;nums1{1, 2, 2, 3, 5, 6} 思路&#xff1a;【双指针】对两个数组设置双指针&#xff0c;依次比较哪…

基于traccar快捷搭建gps轨迹应用

0. 环境 - win10 虚拟机ubuntu18 - i5 ubuntu22笔记本 - USB-GPS模块一台&#xff0c;比如华大北斗TAU1312-232板 - 双笔记本组网设备&#xff1a;路由器&#xff0c;使得win10笔记本ip&#xff1a;192.168.123.x&#xff0c;而i5笔记本IP是192.168.123.215。 - 安卓 手机 1.…

GPT系列总结

1.GPT1 无监督预训练有监督的子任务finetuning https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf 1.1 Unsupervised pre-training &#xff08;1&#xff09;基于一个transformer decoder&#xff0c;通过一个窗口的输入得…

当Visual Studio遇到 “当前不会命中断点.还没有为该文档加载任何符号“的情况

1.配置项目调试路径&#xff1a; 2.问题解决方案&#xff1a; VS配置调试路径不是默认路径时&#xff0c;需要看生成的文件是否在配置路径内&#xff0c;如果不在的话&#xff0c;可能发生"当前不会命中断点.还没有为该文档加载任何符号"的情况&#xff1b; 右键项…

GAN生成对抗模型根据minist数据集生成手写数字图片

文章目录 1.项目介绍2相关网站3具体的代码及结果导入工具包设置超参数定义优化器&#xff0c;以及损失函数训练时的迭代过程训练结果的展示 1.项目介绍 通过用minist数据集进行训练&#xff0c;得到一个GAN模型&#xff0c;可以生成与minist数据集类似的图片。 GAN是一种生成模…