OpenCV的机器视觉包含静态图像处理(2D与3D)与动态视频处理,本主题主要梳理下C++的视频采集处理。
OpenCV视频处理结构
结构图

核心类说明
VideoCapture类
- 负责抓取视频,抓取源支持:照相机,视频文件,图形序列等。
VideoWriter
- 负责写视频数据到文件,包括压缩等。
VideoCapture视频抓取
构造器
- 一共三个构造器
- 默认构造器
- 视频设备构造器
- 视频文件构造器器
- 默认构造器
- 默认构造器没有初始化设备,需要调用open函数初始化设备。
cv::VideoCapture::VideoCapture( )
- 视频设备构造器
cv::VideoCapture::VideoCapture(
int index, // 视频设备编号,从0开始
int apiPreference = CAP_ANY // 视频处理的底层API。FFMPEG,IMAGES,DSHOW
)
- FFMPEG:开源的视频处理框架;
- IMAGES:图片序列(比如:GIF);
- DSHOW:微软的DirectX中视频模块DSHOW;
- 视频文件构造器
cv::VideoCapture::VideoCapture(
const String & filename, // 视频文件路径。
int apiPreference = CAP_ANY // 视频处理的底层API。
)
设备初始化
- 使用open函数实现设备初始化。
- 与构造器相同。
- 使用视频文件初始化
virtual bool open (const String &filename, int apiPreference=CAP_ANY)
- 使用视频设备初始化
virtual bool open (int index, int apiPreference=CAP_ANY)
判别设备是否初始化
- isOpened函数
virtual bool isOpened () const
读取数据
- 读取数据两种方式
- 直接读取
- 多设备情况下,需要先抓取缓冲,然后读取;
直接读取数据
- 使用read函数与操作符读取;
- read函数读取
virtual bool read (OutputArray image)
- OutputArray image返回抓取的视频图像。
-
>>
操作符读取
virtual VideoCapture& cv::VideoCapture::operator >>(Mat & image )
缓冲读取
- 缓冲读取分成两步:
- 使用grap函数抓取到缓冲;
- 使用retrieve函数返回视频图像;
- grap函数
- 抓取视频图像成功,返回true;否则返回false
virtual bool grab ()
- retrieve 函数
- 返回抓取的图像,比read多一个读取标记;
virtual bool retrieve (
OutputArray image,
int flag=0) // 用来指定图像的帧索引,或者底层IP的读取标记;
属性处理
- 提供了set/get来设置与获取设备与底层属性
- set函数
virtual bool set (int propId, double value)
- get函数
virtual double get (int propId) const
释放设备
- 使用release函数释放
virtual void release()
视频抓取应用例子
- 例子的思路:
- 创建一个线程,不同抓取视频图像,然后用信号的方式发送给窗体显示;
抓取图像的线程
- 头文件
#ifndef VIDEO_THRWAD_H
#define VIDEO_THRWAD_H
#include <opencv2/opencv.hpp>
#include <QThread>
class VideoThread : public QThread{
Q_OBJECT
public:
VideoThread();
VideoThread(int deviceid);
~VideoThread();
private:
cv::VideoCapture *device;
protected:
virtual void run();
// 信号
signals:
void video(cv::Mat img);
};
#endif // !VIDEO_THRWAD_H
- 实现文件
#include "videoth.h"
#include<iostream>
// 构造器
VideoThread::VideoThread():
device(new cv::VideoCapture(0)){
}
VideoThread::VideoThread(int deviceid):
device(new cv::VideoCapture(deviceid)){
}
// 析构器
VideoThread::~VideoThread(){
device->release();
delete device;
}
// 线程主方法
void VideoThread::run(){
while(true){
// 抓取图像,发送信息
cv::Mat img;
device->read(img);
emit video(img);
QThread::usleep(100);
}
}
图像处理类
- 头文件
#ifndef IMAGE_PROCESSING_H
#define IMAGE_PROCESSING_H
#include <opencv2/opencv.hpp>
class ImageProcessing{
public:
ImageProcessing();
~ImageProcessing();
public:
void handle_filter2d(cv::InputArray img_src, cv::OutputArray img_out);
};
#endif // !IMAGE_PROCESSING_H
- 实现文件
#include "imageprocessing.h"
ImageProcessing::ImageProcessing(){
}
ImageProcessing::~ImageProcessing(){
}
void ImageProcessing::handle_filter2d(cv::InputArray img_src, cv::OutputArray img_dst){
cv::Mat kernel = (cv::Mat_<int>(3,3) <<
-1,-1,-1,
0, 0, 0,
1, 1, 1);
cv::filter2D(
img_src,
img_dst,
-1, // 输出图像的深度
kernel, // 滤波核
cv::Point2i(-1, -1), // 锚点
64.0, // 正向(加法运算)偏移值
cv::BORDER_REFLECT_101
);
}
UI文件的设计

信号传递的用户类型处理
-
引入头文件
#include<QMetaType>
-
申明需要注册的类型
Q_DECLARE_METATYPE(cv::Mat);
-
注册类型
qRegisterMetaType<cv::Mat>("Mat");
qRegisterMetaType<cv::Mat>("Mat&");
-
在主程序中注册的完整代码
#include <QApplication>
#include "dlgvideo.h"
#include <iostream>
#include<QMetaType>
Q_DECLARE_METATYPE(cv::Mat);
int main(int argc, char* argv[]) {
// 初始化QT应用
QApplication app(argc, argv);
qRegisterMetaType<cv::Mat>("Mat");
qRegisterMetaType<cv::Mat>("Mat&");
DlgVideo dlg;
dlg.show();
return app.exec();
}
对话框类
- 头文件
#ifndef DLG_VIDEO_H
#define DLG_VIDEO_H
#include <opencv2/opencv.hpp>
#include <QDialog>
#include "ui_output.h"
#include "videoth.h"
#include "imageprocessing.h"
class DlgVideo: public QDialog{
Q_OBJECT
public:
DlgVideo(QWidget *parent = 0);
~DlgVideo();
private:
Ui::dlg_video *dlg;
VideoThread *th;
ImageProcessing *proc;
private slots:
void record();
void showImage(cv::Mat img);
};
#endif
- 实现文件
#include "dlgvideo.h"
#include <iostream>
// 构造器与析构器
DlgVideo::DlgVideo(QWidget *parent):
QDialog(parent),
dlg(new Ui::dlg_video()),
th(new VideoThread()),
proc(new ImageProcessing()){
dlg->setupUi(this);
QObject::connect(th, SIGNAL(video(cv::Mat)), this, SLOT(showImage(cv::Mat)));
th->start();
}
DlgVideo::~DlgVideo(){
th->terminate();
delete dlg; // 释放对话框
delete th; // 线程对象释放
delete proc;
}
// 线程信号发过来的图像,并显示
void DlgVideo::showImage(cv::Mat img){
if(img.empty()) return; // 图像非空才显示
cv::Mat img_src;
proc->handle_filter2d(img, img_src);
cv::Mat m_out;
// OpenCV到Qt的颜色格式转换
cv::cvtColor(img_src, m_out, cv::COLOR_BGR2RGBA);
// 转换为Qt图像
QImage i_out(m_out.data, m_out.cols, m_out.rows, QImage::Format_RGBA8888);
// 转换为组件能处理的像素格式
QPixmap p_out = QPixmap::fromImage(i_out);
// 显示图像
dlg->lbl_video->setPixmap(p_out);
// 缩放图像适应组件大小
dlg->lbl_video->setScaledContents(true);
}
// 事件处理
void DlgVideo::record(){
}
Makefile
- 这个文件使用了文件处理函数,来生成moc文件;因为有多个moc文件需要生成,所以还采用了循环。
- opencv的视频处理需要的共享库:
opencv_videoio
INCLUDES =-I/Users/yangqiang/Qt512/5.12.1/Src/qtbase/include \
-I/Users/yangqiang/Qt512/5.12.1/Src/qtbase/include/QtCore \
-I/Users/yangqiang/Qt512/5.12.1/Src/qtbase/include/GtGui \
-I/Users/yangqiang/Qt512/5.12.1/Src/qtbase/include/QtWidgets \
-I/Users/yangqiang/Qt512/5.12.1/Src/qtbase/include/QThread \
-I/usr/local/include/opencv4/
LIBS =-lopencv_core \
-lopencv_highgui \
-lopencv_imgcodecs \
-lopencv_imgproc \
-lopencv_videoio
FRAMEWORKS =-F/Users/yangqiang/Qt512/5.12.1/clang_64/lib \
-framework QtCore \
-framework QtGui \
-framework QtWidgets
LD_ARGS =-Wl,-rpath,/Users/yangqiang/Qt512/5.12.1/clang_64/lib
UICTOOL =/usr/local/Qt-5.12.1/bin/uic
MOCTOOL =/usr/local/Qt-5.12.1/bin/moc
HEADER =dlgvideo.h videoth.h
MOCS =$(HEADER:%.h=moc_%.cpp)
SOURCES =main.cpp dlgvideo.cpp videoth.cpp imageprocessing.cpp
UICS =ui_output.ui
UICH =$(UICS:%.ui=%.h)
main: $(SOURCES) $(UICH) $(MOCS)
g++ -omain $(SOURCES) $(MOCS) -std=c++11 $(INCLUDES) $(LIBS) $(FRAMEWORKS) $(LD_ARGS)
$(MOCS): $(HEADER)
$(foreach VAR, $(HEADER), $(MOCTOOL) -o $(VAR:%.h=moc_%.cpp) $(VAR);)
$(UICH): $(UICS)
$(UICTOOL) -o $(UICS:%.ui=%.h) $(UICS)
clean:
rm -f main $(MOCS) $(UICS:%.ui=%.h) moc_*.cpp

VideoWriter视频存储
- 视频存储的API与VideoCapture类似,主要包含:
- 构造器
- 初始化
virtual bool open (const String &filename, int fourcc, double fps, Size frameSize, bool isColor=true)
- 是否初始化
virtual bool isOpened () const
- 写入
virtual void write (InputArray image)
virtual VideoWriter & operator<< (const Mat &image)
- 释放;
virtual void release ()
- 属性设置
virtual double get (int propId) const
virtual bool set (int propId, double value)
网友评论