中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何進行OpenCV2中的視頻流讀取與處理

發布時間:2022-01-10 00:43:30 來源:億速云 閱讀:219 作者:柒染 欄目:開發技術

這篇文章跟大家分析一下“如何進行OpenCV2中的視頻流讀取與處理”。內容詳細易懂,對“如何進行OpenCV2中的視頻流讀取與處理”感興趣的朋友可以跟著小編的思路慢慢深入來閱讀一下,希望閱讀后能夠對大家有所幫助。下面跟著小編一起深入學習“如何進行OpenCV2中的視頻流讀取與處理”的知識吧。

前言

由于項目需要,計劃實現九路視頻拼接,因此必須熟悉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中的視頻流讀取與處理就分享到這里啦,希望上述內容能夠讓大家有所提升。如果想要學習更多知識,請大家多多留意小編的更新。謝謝大家關注一下億速云網站!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

郎溪县| 托克逊县| 舞阳县| 马尔康县| 德格县| 夏河县| 瑞金市| 应城市| 林芝县| 睢宁县| 萝北县| 宜昌市| 肇源县| 宁强县| 沙雅县| 西盟| 宁明县| 宜都市| 台安县| 保定市| 宝鸡市| 建瓯市| 时尚| 平顶山市| 洪洞县| 汝州市| 巴南区| 金秀| 嘉定区| 蕉岭县| 江城| 汪清县| 赤壁市| 宁陕县| 崇信县| 嘉义市| 朝阳市| 巴彦淖尔市| 柳河县| 翼城县| 屯门区|