c/c++语言开发共享OpenCV2学习笔记之视频流读取与处理

目录前言一. 读取视频序列二. 处理视频帧opencv:打开摄像头获取视频流总结前言由于项目需要,计划实现九路视频拼接,因此必须熟悉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对象时指定视频的文件名)。

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

二. 处理视频帧

为了对视频的每一帧进行处理,这里创建自己的类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();

效果:

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

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视频流读取与处理内容请搜索<计算机技术网(www.ctvol.com)!!>以前的文章或继续浏览下面的相关文章希望大家以后多多支持<计算机技术网(www.ctvol.com)!!>!

需要了解更多c/c++开发分享OpenCV2学习笔记之视频流读取与处理,都可以关注C/C++技术分享栏目—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/1038173.html

(0)
上一篇 2022年1月26日
下一篇 2022年1月26日

精彩推荐