文章目录


使用Opencv处理视频跟踪对象时,借助视频原先的背景与当前的获取帧进行比较,有效地区分开视频中的背景和前景。这种方法我们称为背景减除(

Background Subtraction)。

​OpenCV—python 视频分析背景提取与前景提取​​ 理论介绍。

一、背景消除建模(BSM)与对象跟踪

实现原理:

OpenCV + CPP 系列(圩一)视频分析(BSM,inRange)_opencv


BS算法

  • 图像分割(GMM – 高斯混合模型)​​BackgroundSubtractorMOG2​
  • 机器学习(KNN –K个最近邻)​​BackgroundSubtractorKNN​​ (效果略好于GMM)

createBackgroundSubtractorMOG2(

int history=500,     

double varThreshold=16,

bool detectShadows=true

)


createBackgroundSubtractorKNN(

int history=500,

double dist2Threshold=400.0,

bool detectShadows=true

)

history:用于训练背景的帧数,默认为500帧,如果不手动设置learningRate,history就被用于计算当前的learningRate,此时history越大,learningRate越小,背景更新越慢。

varThreshold:方差阈值,用于判断当前像素是前景还是背景。一般默认16,如果光照变化明显,如阳光下的水面,建议设为25,36,值越大,灵敏度越低;

detectShadows:是否检测影子,设为true为检测,false为不检测,检测影子会增加程序时间复杂度,如无特殊要求,建议设为false

头文件 ​​machine_learning_all.h​​:

#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


class Machine_learning{
public:
void video_analysis(const char* mp4_path);
void video_obj_track(const char* mp4_path);
void video_color_analysis(const char* mp4_path)
};

主函数​​main.cpp​

#include "machine_learning_all.h"


int main(int argc, char** argv) {
const char* mp4_path = "D:\\Desktop\\01.mp4";

Machine_learning ml;
ml.video_analysis(mp4_path);
ml.video_obj_track(mp4_path);
ml.video_color_analysis(mp4_path);

waitKey(0);
destroyAllWindows();
return 0;
}

效果展示

void Machine_learning::video_analysis(const char* mp4_path) {
VideoCapture capture(mp4_path);
if (!capture.isOpened()) {
cout << "opencv video failed!" << endl;
}

// 存储分析后的视频
double fps = capture.get(CAP_PROP_FPS);
Size size = Size(capture.get(CAP_PROP_FRAME_WIDTH), capture.get(CAP_PROP_FRAME_HEIGHT));
VideoWriter writer("D:\\Desktop\\text.mp4", CAP_PROP_FOURCC, fps, size, true);

//初始化BGS
Ptr<BackgroundSubtractor> pMOG2 = cv::createBackgroundSubtractorMOG2();
Ptr<BackgroundSubtractor> pKNN = cv::createBackgroundSubtractorKNN();

Mat frame, bsmask_MOG2, bsmask_KNN;
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
while (capture.read(frame))
{
pMOG2->apply(frame, bsmask_MOG2);
pKNN->apply(frame, bsmask_KNN,-1);

// 消除小块噪声
morphologyEx(bsmask_MOG2, bsmask_MOG2, MORPH_OPEN, kernel);
morphologyEx(bsmask_KNN, bsmask_KNN, MORPH_OPEN, kernel);

// 可视化
imshow("bsmask_MOG2", bsmask_MOG2);
imshow("bsmask_KNN", bsmask_KNN);
writer.write(bsmask_MOG2);
if (waitKey(50) == 27) {
break;
}
}
capture.release();
}

OpenCV + CPP 系列(圩一)视频分析(BSM,inRange)_opencv_02

视频目标跟踪二

void Machine_learning::video_obj_track(const char* mp4_path) {
VideoCapture capture(mp4_path);
if (!capture.isOpened()) {
cout << "video read failed!" << endl;
}

vector<vector<Point>> contours;
Mat frame, mog_mask, hue_img, mask_img, hist_img, back_project;
Ptr<BackgroundSubtractor> pMOG2 = cv::createBackgroundSubtractorMOG2();
Mat Kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
while (capture.read(frame)) {
pMOG2->apply(frame, mog_mask);
threshold(mog_mask, mog_mask, 100, 255, THRESH_BINARY);
morphologyEx(mog_mask, mog_mask, MORPH_OPEN, Kernel);
findContours(mog_mask, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
int count = 0;
for (size_t i = 0; i < contours.size(); i++) {
double area = contourArea(contours[i]);
if (area < 80) {continue;} // 去除块状噪点
Rect selection = boundingRect(contours[i]);
if (selection.width < 30 || selection.height < 30) {continue;} // 去除条状噪点
count++;
rectangle(frame, selection, Scalar(0, 0, 255), 2, 8);
cout << "numText: " << count << endl;
putText(frame, to_string(count), Point(selection.x, selection.y), FONT_HERSHEY_PLAIN, 1, Scalar(255, 0, 0), 1, 8);
}
imshow("frame", frame);
if (waitKey(50) == 27) {
break;
}
}
capture.release();
}

二、基于颜色目标跟踪

pyrMeanShiftFiltering

实现流程

  • inRange过滤
  • 形态学操作提取
  • 轮廓查找
  • 外接矩形获取
  • 位置标定
void Machine_learning::video_color_analysis(const char* mp4_path) {
VideoCapture capture(mp4_path);
if (!capture.isOpened()) {
cout << "opencv video failed!" << endl;
}
Mat frame, mean_img, bsmask_KNN;
vector<vector<Point>> contours;
Rect rect(0, 0, 0, 0);
Point2f center(0, 0);
float radius = 0.0;
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
while (capture.read(frame))
{
//平滑图像 -> 选择颜色区域 -> 消除小块噪声 -> 膨胀检测区域
pyrMeanShiftFiltering(frame, mean_img, 10.0, 10.0);
inRange(mean_img, Scalar(0, 127, 0), Scalar(120, 255, 120), mean_img);
morphologyEx(mean_img, mean_img, MORPH_OPEN, kernel);
dilate(mean_img, mean_img, kernel, Point(-1, -1), 4);

findContours(mean_img, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
if (contours.size() > 0) {
double maxArea = 0;
for (int i = 0; i < contours.size(); i++){
double area = contourArea(contours[i]);
if (area > maxArea) {
maxArea = area;
rect = boundingRect(contours[i]);
minEnclosingCircle(contours[i], center, radius);

rectangle(frame, rect, Scalar(0, 0, 255), 2, 8);
circle(frame, center, radius, Scalar(255, 255, 0), 2, 8);
}
}
}
imshow("mean_img", mean_img);
imshow("frame", frame);
if (waitKey(50) == 27) {
break;
}
}
capture.release();
}

OpenCV + CPP 系列(圩一)视频分析(BSM,inRange)_ide_03