OpenCV (视频读写&&背景消去&&对象跟踪)
Eva.Q Lv9

这是Eva今年暑假学的~

Chapter 1 视频读写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(/*int argc, char** argv*/){ //括号里应该不重要吧
VideoCapture capture;
capture.open("D:/.../"); //要打开的文件的路径
if(!capture.isOpened()){ //如果打开失败了
cout << "could not load" << endl;
return -1;
}

double fps = capture.get(CV_CAP_PROP_FPS);
VedioWriter writer("...", -1, fps, Size(640, 480), true);
//Size size = Size(capture.get(CV_CAP_PROP_FRAME_WIDTH), capture.get(CV_CAP_PROP_FRAME_HEIGHT));

Mat frame, gray, binary;
namedWindow("Video-demo", CV_WINDOW_AUTOSIZE);
while(capture.read(frame)){
//imshow("Video-demo", frame);
//cvtColor(frame, gray, COLOR_BGR2GRAY); //转换成灰度图
//imshow("Video-demo", gray);
//threshold(gray, binary, 0, 255, THRESH_BINARY|THRESH_OTSU); //二值化
//imshow("Video-demo", binary);
bitwise_not(frame, frame); //bitwise_xor,bitwise_and
imshow("Video-demo", frame);
//writer.write(frame);
char c = waitKey(100);
if(c == 27){
break;
}
}

waitKey(0);
return 0;
}

相关类和函数

Capture
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//构造函数:filename – 打开的视频文件名。device – 打开的视频捕获设备id ,如果只有一个摄像头可以填0,表示打开默认的摄像头。 
VideoCapture::VideoCapture();
VideoCapture::VideoCapture(const string& filename);
VideoCapture::VideoCapture(int device);

//open 打开一个视频文件或者打开一个捕获视频的设备
bool VideoCapture::open(const string& filename);
bool VideoCapture::open(int device);
//先实例化再初始化
VideoCapture capture;
capture.open("dog.avi");
//在实例化的同时进行初始化
VideoCapture("dog.avi");

//isopen
bool VideoCapture::isOpened();

//release 关闭视频文件或者摄像头
void VideoCapture::release();

//grab 抓取下一个帧,假如调用成功返回true
bool VideoCapture::grab();

//retrieve 解码并且返回刚刚抓取的视频帧
bool VideoCapture::retrieve(Mat& image, int channel=0);

//read 该函数结合VideoCapture::grab()和VideoCapture::retrieve()其中之一被调用,用于捕获、解码和返回下一个视频帧这是一个最方便的函数对于读取视频文件或者捕获数据从解码和返回刚刚捕获的帧
VideoCapture& VideoCapture::operator>>(Mat& image);
bool VideoCapture::read(Mat& image);
// 方法一
capture.read(frame);
// 方法二
capture.grab();
// 方法三
capture.retrieve(frame);
// 方法四
capture >> frame;

//get 帧率、总帧数、尺寸、格式等,VideoCapture的get方法可以获取这些属性
double VideoCapture::get(int propId);
//参数是属性的ID

//set 设置属性 (属性ID,要设置的值)
bool VideoCapture::set(int propertyId, double value);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

int main(int argc,char* argv[])
{
cv::VideoCapture capture(argv[1]);
if(!capture.isOpened())

{
std::cout<<"video not open."<<std::endl;
return 1;
}
//获取当前视频帧率
double rate = capture.get(CV_CAP_PROP_FPS);
//当前视频帧
cv::Mat frame;
//每一帧之间的延时
//与视频的帧率相对应
int delay = 1000/rate;
bool stop(false);
while(!stop)
{
if(!capture.read(frame))
{
std::cout<<"no video frame"<<std::endl;
break;
}

//此处为添加对视频的每一帧的操作方法
int frame_num = capture.get(CV_CAP_PROP_POS_FRAMES);
std::cout<<"Frame Num : "<<frame_num<<std::endl;
if(frame_num==20)
{
capture.set(CV_CAP_PROP_POS_FRAMES,10);
}

cv::imshow("video",frame);
//引入延时
//也可通过按键停止
if(cv::waitKey(delay)>0)
stop = true;
}


//关闭视频,手动调用析构函数(非必须)
capture.release();
return 0;
}
VideoWriter​ 类
1
2
3
4
5
6
//三种构造函数
VideoWriter::VideoWriter()

VideoWriter::VideoWriter(const String &filename, int fourcc, double fps, Size frameSize, bool isColor=true)

VideoWriter::VideoWriter(const String &filename, int apiPreference, int fourcc, double fps, Size frameSize, bool isColor=true)

filename​ : 输出视频文件的路径名称

fourcc​ : 字符类型的编码,表示用于编码视频文件的编码器。其中

VideoWriter::fourcc('P’,’I’,’M’,’1’) 表示 MPEG-1 编码文件扩展名为 .avi​ ;

VideoWriter::fourcc('X','V','I','D') 表示 MPEG-4 编码文件扩展名为 .avi ;

VideoWriter::fourcc('X',’2','6','4')​ 表示 MPEG-4​ 编码文件扩展名为 .mp4​ ;

VideoWriter::fourcc('I',’4','2','0') 表示 YUV 编码,文件扩展名为.avi ;

VideoWriter::fourcc('M',’P','4','V') 表示旧的 MPEG-4 编码,文件扩展名为 .avi ;

VideoWriter::fourcc('T',’H','E','O') 表示使用 ogg vorbis ,文件扩展名为 .ogv ;

VideoWriter::fourcc('F','L','V','1') 表示 flash video ,文件扩展名为 .flv ;

fps​ : 表示帧率

frameSize​ : 表示每一帧图像的大小

isColor : 灰度图像或者是彩色图像(仅仅在 windows 上支持)

apiPreference : 使用指定的 API,例如可以使用 cv::CAP_FFMPEG 或者 cv::CAP_GSTREAMER​ 等。

1
2
3
4
5
6
7
8
9
10
11
12
//常用函数
VideoWriter::isOpened()

VideoWriter::getBackednName()

VideoWriter::open(const String &filename, int fourcc, double fps, Size frameSize, bool isColor=true);VideoWriter::open(const String &filename,int apiPreference,int fourcc,double fps,Size frameSize,bool isColor=true);

VideoWriter::release()

VideoWriter::get(int propId);

VideoWriter::set(int propId,double value);
Size
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<typename _Tp> class Size_
{
public:
typedef _Tp value_type;
//! various constructors
Size_();
Size_(_Tp _width, _Tp _height);
Size_(const Size_& sz);
Size_(const CvSize& sz);
Size_(const CvSize2D32f& sz);
Size_(const Point_<_Tp>& pt);
Size_& operator = (const Size_& sz);
//! the area (width*height)
_Tp area() const;
//! conversion of another data type.
template<typename _Tp2> operator Size_<_Tp2>() const;
//! conversion to the old-style OpenCV types
operator CvSize() const;
operator CvSize2D32f() const;
_Tp width, height; // the width and the height
};
cvtColor

_CV_8U_ 图像 其通道值范围为0到255

_CV_16U_ 时其值通道值范围为0到65535

_CV_32F_ 时,其通道值范围为0到1

1
2
//src:为原图片 code:需要进行色彩空间转换的结果
void cv::cvtColor (InputArray src, OutputArray dst, int code, int dstCn = 0)
threshold

去掉噪,例如过滤很小或很大像素值的图像点

1
2
3
4
5
6
double threshold( InputArray src, OutputArray dst, double thresh, double maxval, int type )
//src 源图像Mat对象
//dst 目标图像Mat对象
//thresh 设定的阈值
//maxval 是当灰度值大于(或小于)阈值时将该灰度值赋成的值
//type 二值化的方式

二值化的方式, 常用的有如下5种

1
2
3
4
5
CV_THRESH_BINARY      =0,  /**大于阈值的部分被置为255,小于部分被置为0 */
CV_THRESH_BINARY_INV =1, /**大于阈值部分被置为0,小于部分被置为255 */
CV_THRESH_TRUNC =2, /**大于阈值部分被置为threshold,小于部分保持原样 */
CV_THRESH_TOZERO =3, /**小于阈值部分被置为0,大于部分保持不变*/
CV_THRESH_TOZERO_INV =4, /**大于阈值部分被置为0,小于部分保持不变 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<opencv2/opencv.hpp>
using namespace cv;
int main() {
Mat src = imread("C:/Users/Administrator/Desktop/txyzm.png");//引入源图像
if (src.empty()) {
return -1;
}
Mat graySrc,dst;
cvtColor(src, graySrc,CV_BGR2GRAY);//转换为灰度图像
threshold(graySrc, dst, 170, 255, CV_THRESH_BINARY);//图像二值化
imshow("dst", dst);//展示目标图像
waitKey(0);
return 0;
}
bitwise
1
2
3
4
//bitwise_not 将二指图片的效果反转既黑色变白色,白色变黑色
bitwise_not(InputArray src, OutputArray dst, InputArray mask = noArray());
//bitwise_xor 对两个图像进行”异“处理
//bitwise_or 计算每个位操作分离的两个数组或一个数
namedWindow
1
void nameWindow(const string& winname,int flags = WINDOW_AUTOSIZE) ;
  • Qt后端支持标志:

    • WINDOW_NORMALWINDOW_AUTOSIZE
      WINDOW_NORMAL 使您可以调整大小窗口,而 WINDOW_AUTOSIZE 自动调整窗口大小以适应显示图像(参见 imshow ),您无法手动更改窗口大小。
    • WINDOW_FREERATIOWINDOW_KEEPRATIO
      WINDOW_FREERATIO 调整图像不考虑其比例,而 WINDOW_KEEPRATIO 保持图像比例。
    • WINDOW_GUI_NORMALWINDOW_GUI_EXPANDED
      WINDOW_GUI_NORMAL 是绘制窗口的旧方法没有状态栏和工具栏,而 WINDOW_GUI_EXPANDED 是一个新的增强 GUI
      默认情况下,flags == WINDOW_AUTOSIZE | WINDOW_KEEPRATIO |WINDOW_GUI_EXPANDED

Chapter 2 背景消去建模(BSM):背景不常变化的

图像分割 (GMM — 高斯混合模型)

1
2
3
4
5
6
7
8
9
10
11
Ptr<BackgroundSubtractorMOG2> bgsubtractor = createBackgroundSubtractorMOG2();
bgsubtractor->setHistory(20);
bgsubtractor->setVarThreshold(100);
bgsubtractor->setDetectShadows(true);
bgsubtractor->setBackgroundRatio(4);
bgsubtractor->setNMixtures(5);
bgsubtractor->setShadowThreshold(40);
bgsubtractor->setVarInit(15);
bgsubtractor->setVarMax(20);
bgsubtractor->setVarMin(4);
bgsubtractor->setVarThresholdGen(100);

History:用于训练背景的帧数,history可以用于计算当前的learning rate ,history越大,learning rate越低,背景更新越缓慢;

VarThreshold:方差阈值,主要用于判断前景还是背景,值越大,灵敏度越低

DetectShadows:是否检测有影子,开启后会增加算法复杂度

NMixtures:高斯模型个数,默认5个,最多8个,模型数越多,耗时越长

BackgroundRatio:高斯背景模型权重和阈值,nmixtures个模型按权重排序后,只取模型权重累加值大于backgroundRatio的前几个作为背景模型

VarInit:新建高斯模型的方差初始值,默认15

VarMax:背景更新时,用于限制高斯模型方差的最大值,默认20

VarMin:背景更新时,用于限制高斯模型方差的最小值,默认4

VarThresholdGen:方差阈值,用于已经存在的匹配的模型,如果不存在则新建一个

原文链接:https://blog.csdn.net/holecloud/article/details/80139297

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(){
VideoCapture capture;
capture.open("...");
if(!capture.isOpened()){ //如果打开失败了
cout << "could not load" << endl;
return -1;
}

Mat frame;
Mat bsmaskMOG2;
namedWindow("input video", CV_WINDOW_AUTOSIZE);
namedWindow("MOG2", CV_WINDOW_AUTOSIZE);
Ptr<BackgroudSubtractor> pMOG2 = createBackgroundSubtractorMOG2(); //选择API
while(capture.read(frame)){
imshow("inpupt video", frame);
pMOG2 -> apply(frame, bsmaskMOG2);
imshow("MOG2", bsmaskMOG2);
char c = waitKey(100);
if(c == 27){
break;
}
}

capture.release();
waitKey(0);
return 0;
}

机器学习(KNNK​ 个最近邻)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(){
VideoCapture capture;
capture.open("...");
if(!capture.isOpened()){ //如果打开失败了
cout << "could not load" << endl;
return -1;
}

Mat frame;
Mat bsmaskKNN;
namedWindow("input video", CV_WINDOW_AUTOSIZE);
namedWindow("KNN", CV_WINDOW_AUTOSIZE);
Ptr<BackgroudSubtractor> pKNN = createBackgroundSubtractorKNN();
while(capture.read(frame)){
imshow("inpupt video", frame);
pKNN -> apply(frame, bsmaskKNN);
imshow("KNN", bsmaskKNN);
char c = waitKey(100);
if(c == 27){
break;
}
}

capture.release();
waitKey(0);
return 0;
}

开操作去噪声

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(){
VideoCapture capture;
capture.open("...");
if(!capture.isOpened()){ //如果打开失败了
cout << "could not load" << endl;
return -1;
}

Mat frame;
Mat bsmaskMOG2;
namedWindow("input video", CV_WINDOW_AUTOSIZE);
namedWindow("MOG2", CV_WINDOW_AUTOSIZE);

Mat kernel = getStructuringELement(MORPH_RECT, Size(3, 3), Point(-1, -1));

Ptr<BackgroudSubtractor> pMOG2 = createBackgroundSubtractorMOG2(); //选择API
while(capture.read(frame)){
imshow("inpupt video", frame);
pMOG2 -> apply(frame, bsmaskMOG2);
morphologyEx(bsmaskMOG2, bsmaskMOG2, MORPH_OPEN, kernel, Point(-1, -1));
imshow("MOG2", bsmaskMOG2);
char c = waitKey(50);
if(c == 27){
break;
}
}

capture.release();
waitKey(0);
return 0;
}

相关类和函数

BackgroundSubtractor

1
2
3
BackgroundSubtractor (父类)
- BackgroundSubtractorMOG2
- BackgroundSubtractorKNN

Chapter 3 对象检测与跟踪:基于颜色

inRange过滤

利用颜色进行过滤

形态学操作提取

开操作 去噪声 膨胀

轮廓查找
外接矩形获取
位置标定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

Rect roi;
void processFrame(Mat &mask, Rect &rect);

int main(){
VideoCapture capture;
capture.open("...");
if(!capture.isOpened()){ //如果打开失败了
cout << "could not load" << endl;
return -1;
}

Mat frame, mask;
Mat kernel1 = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
Mat kernel2 = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));

namedWindow("input video", CV_WINDOW_AUTOSIZE);
namedWindow("track mask", CV_WINDOW_AUTOSIZE);
while(capture.read(frame)){
inRange(frame, Scalar(0, 0, 127), Scalar(120, 255, 120), mask); //过滤
morphologyEx(mask, mask, MORPH_OPEN, kernel1, Point(-1, -1), 1); //开操作
imshow("track mask", mask);
dilate(mask, mask, kernel2, Point(-1, -1), 4); //膨胀
imshow("dilate mask", mask);

processFrame(mask, roi); //找轮廓并标定

rectangle(frame, roi, Scalar(0, 0, 255), 3, 8, 0);

imshow("input video", frame);
char c = waitKey(50);
if(c == 27){
break;
}
}

capture.release();
waitKey(0);
return 0;
}

void processFrame(Mat &mask, Rect &rect){ //查找轮廓
vector<vector<Point> > contours;
vector<Vec4i> hireachy;
findContours(mask, contours, hireachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
if(contours.size() > 0){
double maxArea = 0.0;
for(size_t t = 0; t < contours.size(); ++t){
double area = contourArea(contours[static_cast<int>(t)]);
if(area > maxArea){
maxArea = area;
rect = boundingRect(contours[static_cast<int>(t)]);
}
}
}else{
rect.x = rect.y = rect.width = rect.height = 0;
}
}

相关类和函数

inRange()

OpenCV中的inRange()函数可实现二值化功能(这点类似threshold()函数),更关键的是可以同时针对多通道进行操作,使用起来非常方便!主要是将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0),该功能类似于之间所讲的双阈值化操作。

1
2
3
4
5
  void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);
//src 输入要处理的图像,可以为单通道或多通道
//lowerb 包含下边界的数组或标量
//upperb 包含上边界数组或标量
//dst 输出图像,与输入图像src 尺寸相同且为CV_8U 类型

Checks if array elements lie between the elements of two other arrays.即检查数组元素是否在另外两个数组元素值之间。这里的数组通常也就是矩阵Mat或向量。

该函数输出的dst是一幅二值化之后的图像。

morphologyEx

形态学变化函数

(4条消息) opencv 形态学变换 morphologyEx函数_keen_zuxwang的博客-CSDN博客

findContours

检测物体轮廓

1
2
3
4
5
6
7
findContours(InputOutputArray image, OutputArrayOfArrays contours,  OutputArray hierarchy, int mode, int method, Point offset = Point());
//image 单通道图像矩阵 灰度图or二值图像(Canny、拉普拉斯等边缘检测算子)
//contours 向量vector<vector<Point>> contours
//hierarchy 向量vector<Vec4i> hierarchy vec4i:typedef Vec<int, 4> Vec4i;
//mode 轮廓检索模式
//method 定义轮廓的近似方法
//Point偏移量 所有的轮廓信息相对于原始图像对应点的偏移量 相当于在每一个检测出的轮廓点上加上该偏移量 并且Point还可以是负值!

轮廓检索模式

1
2
3
4
CV_RETR_EXTERNAL //只检测最外围轮廓
CV_RETR_LIST //检测所有的轮廓 轮廓不建立等级关系 即不存在父轮廓或内嵌轮廓
CV_RETR_CCOMP //检测所有的轮廓 建立两个等级关系
CV_RETR_TREE //检测所有轮廓 所有轮廓建立一个等级树结构

定义轮廓的近似方法

1
2
3
4
CV_CHAIN_APPROX_NONE //保存物体边界上所有连续的轮廓点到contours向量内
CV_CHAIN_APPROX_SIMPLE //仅保存轮廓的拐点信息 把所有轮廓拐点处的点保存入contours
CV_CHAIN_APPROX_TC89_L1 //使用teh-Chinl chain 近似算法
CV_CHAIN_APPROX_TC89_KCOS //使用teh-Chinl chain 近似算法

contourArea

计算轮廓面积

1
2
3
double contourArea(InputArray contour, bool oriented = false);
//contour 输入的二维点集(轮廓顶点)可以是 vector 或 Mat 类型
//riented,面向区域标识符 有默认值 false 若为 true 该函数返回一个带符号的面积值 正负取决于轮廓的方向(顺时针还是逆时针) 若为 false 表示以绝对值返回

arcLength

计算封闭轮廓周长

1
2
3
double arcLength(InputArray curve, bool closed);
//contour 输入的二维点集(轮廓顶点)可以是 vector 或 Mat 类型
//closed 用于指示曲线是否封闭

boundingRect

计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的

1
2
Rect boundingRect(InputArray points);
//points 输入的二维点集(轮廓顶点)可以是 vector 或 Mat 类型
  • Post title:OpenCV (视频读写&&背景消去&&对象跟踪)
  • Post author:Eva.Q
  • Create time:2021-07-28 10:49:38
  • Post link:https://qyy/2021/07/28/OPENCV/OPENCV1-2/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.