Deux méthodes de streaming courantes pour le traitement des images

Avant le traitement des images, les algorithmes traditionnels ou l’apprentissage en profondeur collectent toujours d’abord les images, ce qu’on appelle le flux pull. Il existe deux façons de résoudre le problème de l'extraction de flux. L'une consiste à utiliser directement opencv pour obtenir le flux, et l'autre consiste à utiliser ffmpeg pour obtenir le flux. Les deux méthodes pour extraire le flux sont présentées ci-dessous.

1. Opencv prend le flux directement

La méthode d'acquisition de flux d'Opencv utilise principalement la classe VideoCapture pour le traitement. VideoCapture fournit un ensemble complet de solutions pour lire les informations du flux vidéo. Les principales fonctions sont les suivantes :
VideoCapture a trois constructeurs :

  • Constructeur sans aucun paramètre
    Insérer la description de l'image ici
  • Constructeur avec une adresse de flux vidéo
    Insérer la description de l'image ici
  • La
    Insérer la description de l'image ici
    fonction constructeur isOpened() avec un index vidéo est principalement utilisée pour déterminer si l'adresse du flux est ouverte avec succès.
    Insérer la description de l'image ici
    read() lit les données vidéo.
    Insérer la description de l'image ici
    La fonction release() est utilisée pour libérer l'objet de classe.
    Insérer la description de l'image ici
    Adresse de référence spécifique : https : //docs.opencv.org/4.0.0/d8/dfe/classcv_1_1VideoCapture.html

1.1Diffusion Python

Le processus principal est divisé en les étapes suivantes :

  • Instanciez la classe VideoCapture par l'adresse du flux
  • Déterminer si l'adresse du flux est ouverte avec succès
  • Parcourez chaque image de flux de données et traitez-la
  • Libérer l'objet instancié
  • versioncv
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 Flux d'extraction opencv C++

La façon dont C++ utilise opencv pour extraire des flux est fondamentalement la même qu'opencv (ps : la couche inférieure de python doit être implémentée en C++), donc son format d'implémentation est le suivant :

	std::string vedio_path = "rtsp://admin:[email protected]/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. streaming ffmpeg (implémentation C++)

  • Téléchargez l'
    adresse de téléchargement du package ffmpeg du package ffmpeg.
    La version 5.1.2 téléchargée par le blogueur
    Insérer la description de l'image ici

  • Utilisez la configuration vs2022.
    Ajoutez le chemin d'inclusion du package ffmpeg nouvellement téléchargé dans C/C++> Répertoire d'inclusion supplémentaire.
    Insérer la description de l'image ici
    Ajoutez le chemin du fichier lib du package ffmpeg dans Linker-> Répertoire de bibliothèque supplémentaire.
    Insérer la description de l'image ici
    Dans Linker-> Entrée-> Dépendances supplémentaires. Ajoutez le répertoire de bibliothèque lib requis et organisez-le comme suit :

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

    Si vous ne souhaitez pas configurer le répertoire du fichier bin dans ffmpeg dans la variable d'environnement, vous pouvez utiliser la méthode suivante pour le configurer temporairement : Utilisez
    Path=D:\ffmpeg\bin;%PATH dans Debug->Environment pour utiliser temporairement.
    Insérer la description de l'image ici

Le blogueur a encapsulé la méthode de streaming dans une classe, et le code principal est le suivant :
Le fichier ffmpeg.h est le suivant :

#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

Le fichier ffmpeg.cpp spécifique implémenté est le suivant

#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:[email protected]: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;

}

Ce qui précède est une méthode très simple pour extraire des flux. Elle ne peut être utilisée que comme démonstration pour implémenter la lecture de flux. Si vous souhaitez obtenir un streaming et un traitement en temps réel, vous devez utiliser le multithread pour implémenter un thread pour lire le flux. données.Mettez les données dans la file d'attente et implémentez en même temps un thread pour lire les données du flux, lire les données de la file d'attente et exécuter en même temps.

annexe

En fait, opencv peut également utiliser ffmpeg pour extraire des flux, mais vous devez spécifier la version de ffmpeg lors de la compilation d'opencv.

Je suppose que tu aimes

Origine blog.csdn.net/caobin_cumt/article/details/132384678
conseillé
Classement