目录
- 前言
- 一. 读取视频序列
- 二. 处理视频帧
- 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视频流读取与处理内容请搜索<计算机技术网(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