返回顶部
首页 > 资讯 > 后端开发 > Python >OpenCV2学习笔记之视频流读取与处理
  • 126
分享到

OpenCV2学习笔记之视频流读取与处理

2024-04-02 19:04:59 126人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录前言一. 读取视频序列二. 处理视频帧OpenCV:打开摄像头获取视频流 总结 前言 由于项目需要,计划实现九路视频拼接,因此必须熟悉OpenCV对视频序列的

前言

由于项目需要,计划实现九路视频拼接,因此必须熟悉OpenCV对视频序列的处理。视频信号处理是图像处理的一个延伸,所谓的视频序列是由按一定顺序进行排放的图像组成,即帧(Frame)。在这里,主要记录下如何使用Qt+OpenCV读取视频中的每一帧,之后,在这基础上将一些图像处理的算法运用到每一帧上(如使用Canny算子检测视频中的边缘)。

一. 读取视频序列

OpenCV提供了一个简便易用的框架以提取视频文件和USB摄像头中的图像帧,如果只是单单想读取某个视频,你只需要创建一个cv::VideoCapture实例,然后在循环中提取每一帧。新建一个Qt控制台项目,直接在main函数添加:

#include <QCoreApplication>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <QDebug>

int main(int arGC, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 读取视频流
    cv::VideoCapture capture("e:/BrokeGirls.mkv");
    // 检测视频是否读取成功
    if (!capture.isOpened())
    {
        qDebug() << "No Input Image";
        return 1;
    }

    // 获取图像帧率
    double rate= capture.get(CV_CAP_PROP_FPS);
    bool stop(false);
    cv::Mat frame; // 当前视频帧
    cv::namedWindow("Extracted Frame");

    // 每一帧之间的延迟
    int delay= 1000/rate;

    // 遍历每一帧
    while (!stop)
    {
        // 尝试读取下一帧
        if (!capture.read(frame))
            break;
        cv::imshow("Extracted Frame",frame);
        // 引入延迟
        if (cv::waiTKEy(delay)>=0)
                stop= true;
    }
        return a.exec();
}

(注意:要正确打开视频文件,计算机中必须安装有对应的解码器,否则cv::VideoCapture无法理解视频格式!)运行后,将出现一个窗口,播放选定的视频(需要在创建cv::VideoCapture对象时指定视频的文件名)。

这里写图片描述

二. 处理视频帧

为了对视频的每一帧进行处理,这里创建自己的类VideoProcessor,其中封装了OpenCV的视频获取框架,该类允许我们指定每帧调用的处理函数。

首先,我们希望指定一个回调处理函数,每一帧中都将调用它。该函数接受一个cv::Mat对象,并输出处理后的cv::Mat对象,其函数签名如下:

void processFrame(cv::Mat& img, cv::Mat& out);

作为这样一个处理函数的例子,以下的Canny函数计算图像的边缘,使用时直接添加在mian文件中即可:

    // 对视频的每帧做Canny算子边缘检测
void canny(cv::Mat& img, cv::Mat& out) 
{
    // 先要把每帧图像转化为灰度图
    cv::cvtColor(img,out,CV_BGR2GRAY);
    // 调用Canny函数
    cv::Canny(out,out,100,200);
    // 对像素进行翻转
    cv::threshold(out,out,128,255,cv::THRESH_BINARY_INV);
}

现在我们需要创建一个VideoProcessor类,用来部署视频处理模块。而在此之前,需要先另外创建一个类,即VideoProcessor内部使用的帧处理类。这是因为在面向对象的上下文中,更适合使用帧处理类而不是一个帧处理函数,而使用类可以给程序员在涉及算法方面有更多的灵活度(书上介绍的)。将这个内部帧处理类命名为FrameProcessor,其定义如下:

#ifndef FRAMEPROCESSOR_H
#define FRAMEPROCESSOR_H
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

class FrameProcessor
{
public:
    virtual void process(cv:: Mat &input, cv:: Mat &output)= 0;
};

#endif // FRAMEPROCESSOR_H

现在可以开始定义VideoProcessor类了,以下为videoprocessor.h中的内容:

#ifndef VIDEOPROCESSOR_H
#define VIDEOPROCESSOR_H
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <QDebug>
#include "frameprocessor.h"

class VideoProcessor
{
  private:
      // 创建视频捕获对象
      cv::VideoCapture capture;
      // 每帧调用的回调函数
      void (*process)(cv::Mat&, cv::Mat&);
      // FrameProcessor接口
      FrameProcessor *frameProcessor;
      // 确定是否调用回调函数的bool信号
      bool callIt;
      // 输入窗口的名称
      std::string windowNameInput;
      // 输出窗口的名称
      std::string windowNameOutput;
      // 延迟
      int delay;
      // 已处理的帧数
      long fnumber;
      // 在该帧停止
      long frameToStop;
      // 是否停止处理
      bool stop;

      // 当输入图像序列存储在不同文件中时,可使用以下设置
      // 把图像文件名的数组作为输入
      std::vector<std::string> images;
      // 图像向量的迭加器
      std::vector<std::string>::const_iterator itImg;

      // 得到下一帧
      // 可能来自:视频文件或摄像头
      bool readNextFrame(cv::Mat &frame)
      {
          if (images.size()==0)
              return capture.read(frame);
          else {

              if (itImg != images.end())
              {
                  frame= cv::imread(*itImg);
                  itImg++;
                  return frame.data != 0;
              }
          }
      }

public:

      // 默认设置 digits(0), frameToStop(-1),
      VideoProcessor() : callIt(false), delay(-1),
          fnumber(0), stop(false),
          process(0), frameProcessor(0) {}

      // 创建输入窗口
      void displayInput(std::string wt);
      // 创建输出窗口
      void displayOutput(std::string wn);
      // 不再显示处理后的帧
      void dontDisplay();

      // 以下三个函数设置输入的图像向量
      bool setInput(std::string filename);
      // 若输入为摄像头,设置ID
      bool setInput(int id);
      // 若输入为一组图像序列时,应用该函数
      bool setInput(const std::vector<std::string>& imgs);

      // 设置帧之间的延迟
      // 0意味着在每一帧都等待按键响应
      // 负数意味着没有延迟
      void setDelay(int d);

      // 返回图像的帧率
      double getFrameRate();

      // 需要调用回调函数
      void callProcess();

      // 不需要调用回调函数
      void dontCallProcess();

      // 设置FrameProcessor实例
      void setFrameProcessor(FrameProcessor* frameProcessorPtr);

      // 设置回调函数
      void setFrameProcessor(void (*frameProcessingCallback)(cv::Mat&, cv::Mat&));

      // 停止运行
      void stopIt();

      // 判断是否已经停止
      bool isStopped();

      // 是否开始了捕获设备?
      bool isOpened();

      // 返回下一帧的帧数
      long getFrameNumber();

      // 该函数获取并处理视频帧
      void run();

};

#endif // VIDEOPROCESSOR_H

然后,在videoprocessor.cpp中定义各个函数的功能:

#include "videoprocessor.h"

// 创建输入窗口
void VideoProcessor::displayInput(std::string wt)
{
    windowNameInput= wt;
    cv::namedWindow(windowNameInput);
}

// 创建输出窗口
void VideoProcessor::displayOutput(std::string wn)
{
    windowNameOutput= wn;
    cv::namedWindow(windowNameOutput);
}

// 不再显示处理后的帧
void VideoProcessor::dontDisplay()
{
    cv::destroyWindow(windowNameInput);
    cv::destroyWindow(windowNameOutput);
    windowNameInput.clear();
    windowNameOutput.clear();
}

// 设置输入的图像向量
bool VideoProcessor::setInput(std::string filename)
{
  fnumber= 0;
  // 释放之前打开过的视频资源
  capture.release();
  images.clear();

  // 打开视频
  return capture.open(filename);
}

// 若输入为摄像头,设置ID
bool VideoProcessor::setInput(int id)
{
  fnumber= 0;
  // 释放之前打开过的视频资源
  capture.release();
  images.clear();

  // 打开视频文件
  return capture.open(id);
}

// 若输入为一组图像序列时,应用该函数
bool VideoProcessor::setInput(const std::vector<std::string>& imgs)
{
  fnumber= 0;
  // 释放之前打开过的视频资源
  capture.release();

  // 输入将是该图像的向量
  images= imgs;
  itImg= images.begin();

  return true;
}

// 设置帧之间的延迟
// 0意味着在每一帧都等待按键响应
// 负数意味着没有延迟
void VideoProcessor::setDelay(int d)
{
    delay= d;
}

// 返回图像的帧率
double VideoProcessor::getFrameRate()
{
    if (images.size()!=0) return 0;
    double r= capture.get(CV_CAP_PROP_FPS);
    return r;
}

// 需要调用回调函数
void VideoProcessor::callProcess()
{
    callIt= true;
}

// 不需要调用回调函数
void VideoProcessor::dontCallProcess()
{
    callIt= false;
}

// 设置FrameProcessor实例
void VideoProcessor::setFrameProcessor(FrameProcessor* frameProcessorPtr)
{
    // 使回调函数无效化
    process= 0;
    // 重新设置FrameProcessor实例
    frameProcessor= frameProcessorPtr;
    callProcess();
}

// 设置回调函数
void VideoProcessor::setFrameProcessor(void (*frameProcessingCallback)(cv::Mat&, cv::Mat&))
{
    // 使FrameProcessor实例无效化
    frameProcessor= 0;
    // 重新设置回调函数
    process= frameProcessingCallback;
    callProcess();
}

// 以下函数表示视频的读取状态
// 停止运行
void VideoProcessor::stopIt()
{
    stop= true;
}

// 判断是否已经停止
bool VideoProcessor::isStopped()
{
    return stop;
}

// 是否开始了捕获设备?
bool VideoProcessor::isOpened()
{
    return capture.isOpened() || !images.empty();
}

// 返回下一帧的帧数
long VideoProcessor::getFrameNumber()
{
  if (images.size()==0)
  {
      // 得到捕获设备的信息
      long f= static_cast<long>(capture.get(CV_CAP_PROP_POS_FRAMES));
      return f;

  }
  else // 当输入来自一组图像序列时的情况
  {
      return static_cast<long>(itImg-images.begin());
  }
}

// 该函数获取并处理视频帧
void VideoProcessor::run()
{
    // 当前帧
    cv::Mat frame;
    // 输出帧
    cv::Mat output;

    // 打开失败时
    if (!isOpened())
    {
        qDebug() << "Error!";
        return;
    }
    stop= false;
    while (!isStopped())
    {
        // 读取下一帧
        if (!readNextFrame(frame))
            break;
        // 显示输出帧
        if (windowNameInput.length()!=0)
            cv::imshow(windowNameInput,frame);
        // 调用处理函数
        if (callIt)
        {
          // 处理当前帧
          if (process)
              process(frame, output);
          else if (frameProcessor)
              frameProcessor->process(frame,output);
          // 增加帧数
          fnumber++;
        }
        else
        {
          output= frame;
        }
        // 显示输出帧
        if (windowNameOutput.length()!=0)
            cv::imshow(windowNameOutput,output);
        // 引入延迟
        if (delay>=0 && cv::waitKey(delay)>=0)
          stopIt();
        // 检查是否需要停止运行
        if (frameToStop>=0 && getFrameNumber()==frameToStop)
            stopIt();
    }
}

定义好视频处理类,它将与一个回调函数相关联。使用该类,可以创建一个实例,指定输入的视频文件,绑定回调函数,然后开始对每一帧进行处理,要调用这个视频处理类,只需在main函数中添加:

    // 定义一个视频处理类处理视频帧
    // 首先创建实例
    VideoProcessor processor;
    // 打开视频文件
    processor.setInput("e:/BrokeGirls.mkv");
    // 声明显示窗口
    // 分别为输入和输出视频
    processor.displayInput("Input Video");
    processor.displayOutput("Output Video");
    // 以原始帧率播放视频
    processor.setDelay(1000./processor.getFrameRate());
    // 设置处理回调函数
    processor.setFrameProcessor(canny);
    // 开始帧处理过程
    processor.run();
    cv::waitKey();

效果:

这里写图片描述

OpenCV:打开摄像头获取视频流 

#include

#include

using namespace cv;

using namespace std;

int main()

{

//【1】从摄像头读入视频

    VideoCapture capture(1);

    if (!capture.isOpened())

{

cout<< "open camera fail ..." << endl;

        return -1;

    }

capture.set(CAP_PROP_FRAME_WIDTH, 640);

    capture.set(CAP_PROP_FRAME_HEIGHT, 480);

    char filename[200];

    int count =0;

    //【2】循环显示每一帧

    Mat frame;  //定义一个Mat变量,用于存储每一帧的图像

    char key;

    while (true)

{

//读入图像

        capture>> frame;  //读取当前帧

        key = waitKey(20);

        if(key ==27)//esc键退出

            break;

        if(key ==32)//空格键保存图像

        {

sprintf(filename, "Picture_%d.png", ++count);

            imwrite(filename, frame);//

            namedWindow("[frame]", WINDOW_NORMAL);

            imshow("[frame]",frame);

        }

imshow("image", frame);  //显示当前帧

    }

return 0;

}

总结 

到此这篇关于OpenCV2学习笔记之视频流读取与处理的文章就介绍到这了,更多相关OpenCV视频流读取与处理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: OpenCV2学习笔记之视频流读取与处理

本文链接: https://lsjlt.com/news/161947.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • OpenCV2学习笔记之视频流读取与处理
    目录前言一. 读取视频序列二. 处理视频帧OpenCV:打开摄像头获取视频流 总结 前言 由于项目需要,计划实现九路视频拼接,因此必须熟悉OpenCV对视频序列的...
    99+
    2024-04-02
  • 如何进行OpenCV2中的视频流读取与处理
    这篇文章跟大家分析一下“如何进行OpenCV2中的视频流读取与处理”。内容详细易懂,对“如何进行OpenCV2中的视频流读取与处理”感兴趣的朋友可以跟着小编的思路慢慢深入来阅读一下,希望阅读后能够对大家有所帮助。下面跟着小编一起深入学习“如...
    99+
    2023-06-26
  • tensorflow学习笔记之tfrecord文件的生成与读取
    训练模型时,我们并不是直接将图像送入模型,而是先将图像转换为tfrecord文件,再将tfrecord文件送入模型。为进一步理解tfrecord文件,本例先将6幅图像及其标签转换为t...
    99+
    2024-04-02
  • PHP学习笔记:音乐播放器与视频平台
    引言:在当今数字化时代,音乐和视频成为了人们娱乐和传播信息的重要方式。而作为Web开发者,学习如何构建音乐播放器和视频平台是必不可少的一部分。本文将介绍如何使用PHP语言构建一个简单的音乐播放器和视频平台,并给出具体的代码示例。音乐播放器1...
    99+
    2023-10-21
    PHP (关键词:PHP)
  • C++学习笔记之浅谈异常处理
    异常处理主要是针对能通过编译但是运行是在某个特定条件下会出现异常,程序崩溃,结果出错。来进行的东西 C++处理异常的机制是由3个部分组成的,即检查(try)、抛出(throw)和捕捉...
    99+
    2024-04-02
  • PHP学习笔记:日期与时间处理
    在Web开发中,日期和时间处理是非常常见的需求。无论是显示文章发布时间、倒计时功能、还是日程安排,都需要对日期和时间进行处理。PHP作为一门广泛应用于Web开发的编程语言,提供了丰富的日期和时间处理函数和方法,便于开发者进行相关操作。获取当...
    99+
    2023-10-21
    PHP编程 时间处理 日期处理
  • Android学习笔记之Handler处理机制的Looper
    Handler处理机制中,需要Looper来动态的进行循环,以此来不断将MessageQueue的数据取出。 在主线程中,Looper和Mess...
    99+
    2022-06-06
    android学习 looper handler Android
  • android学习笔记之handler消息处理机制1
    在andorid中,一个进程中最少也有一个线程,一般来说,有一个主线程(也就UI线程);以及多个子线程(也叫Worker线程)。 在androi...
    99+
    2022-06-06
    android学习 handler Android
  • PHP学习笔记:人脸识别与图像处理
    前言:随着人工智能技术的发展,人脸识别和图像处理成为了热门话题。在实际应用中,人脸识别与图像处理多用于安全监控、人脸解锁、卡牌比对等方面。而PHP作为一种常用的服务器端脚本语言,也可以用来实现人脸识别与图像处理的相关功能。本篇文章将带你了解...
    99+
    2023-10-21
    人脸识别 图像处理 PHP
  • PHP学习笔记:表单处理与数据验证
    在网页开发中,表单是用户与网站进行交互的重要组件之一。当用户在网站上填写表单并提交数据时,网站需要对提交的数据进行处理和验证,确保数据的准确性和安全性。本文将介绍如何使用PHP来处理表单和进行数据验证,并提供具体的代码示例。表单提交和数据预...
    99+
    2023-10-21
    数据验证 表单处理 PHP学习
  • PHP学习笔记:异常处理与错误调试
    在编写PHP代码的过程中,难免会遇到各种错误和异常。良好的异常处理和错误调试能够帮助我们更好地定位问题和修复bug,提高代码的可靠性和稳定性。本文将介绍PHP中的异常处理和错误调试的具体方法,并给出相关的代码示例。一、异常处理异常的概念在程...
    99+
    2023-10-21
    错误调试 异常处理 关键词:PHP
  • JavaScript学习笔记之取值函数getter与取值函数setter详解
    目录取值函数getter和存值函数setter使用get与set函数有两个好处取值函数getter和存值函数setter get和set是两个关键字,用于对某个属性设置存值函数和取值...
    99+
    2024-04-02
  • PHP学习笔记:图像处理与GD库的使用
    引言:在现代互联网的世界中,图像处理已经成为了一个重要的技术。无论是网页设计、移动应用还是电子商务平台,图像处理都扮演着不可或缺的角色。PHP作为一种广泛应用于网络开发的脚本语言,具备强大的图像处理能力和广泛的库支持,其中最常用的就是GD库...
    99+
    2023-10-21
    学习 图像处理 PHP
  • PHP学习笔记:字符串处理与正则表达式
    在PHP开发中,字符串处理和正则表达式是非常重要的技巧之一。无论是对用户输入的数据进行处理,还是对文本进行搜索和替换,字符串处理和正则表达式都能帮助我们实现更加灵活和高效的操作。本文将介绍一些常用的字符串处理函数和正则表达式的用法,并提供具...
    99+
    2023-10-21
    正则表达式 处理 PHP 学习笔记:字符串
  • 黑马程序员Spring视频教程,全面深度讲解spring5底层原理 学习笔记
    介绍 代码仓库地址:https://gitee.com/CandyWall/spring-source-study 跟着黑马满一航老师的spring高级49讲做的学习笔记,本笔记跟视频内容的项目名称和...
    99+
    2023-09-29
    java spring spring boot
  • Activiti工作流学习笔记之自动生成28张数据库表的底层原理解析
    网上关于工作流引擎Activiti生成表的机制大多仅限于四种策略模式,但其底层是如何实现的,相关文章还是比较少,因此,觉得撸一撸其生成表机制的底层原理。 我接触工作流引擎Activi...
    99+
    2024-04-02
  • Go语言处理大数据:这些Unix命令和学习笔记能助你一臂之力!
    随着大数据时代的到来,数据处理的需求也越来越迫切。而Go语言作为一门高效、强类型的编程语言,正逐渐成为处理大数据的首选语言之一。本文将介绍Go语言处理大数据的常用Unix命令和学习笔记,帮助读者更好地应对大数据处理的挑战。 一、Unix命...
    99+
    2023-09-14
    大数据 学习笔记 unix
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作