一、背景消除概述
📌 背景减除(Background Subtraction)是许多基于计算机视觉的任务中的主要预处理步骤。
📌背景减除(BS)是通过使用静态摄像机来生成前景掩码(即,包含属于场景中的移动对象的像素的二进制图像)的常用和广泛使用的技术。
📌BS计算当前帧和背景模型之间的减法的前景掩码,该背景模型包含场景的静态部分,或者更一般地,在给定观察到的场景的特征的情况下,可以将其视为背景的所有内容。
如果有完整的静止的背景帧,可以通过帧差法来计算像素差从而获取到前景对象。但是在大多数情况下,没有这样的图像,所以需要从拥有的任何图像中提取背景。当运动物体有阴影时,由于阴影也在移动,情况会变的变得更加复杂。为此引入了背景减除算法,通过这一方法我们能够从视频中分离出运动的物体前景,从而达到目标检测的目的。
二、OpenCV中提供的背景消除法
1、背景建模包括两个主要步骤
①后台初始化
计算背景的初始模型。
②背景更新
更新模型以适应场景中的可能变化。
2、基于高斯混合模型GMM实现背景提取
-
GMM原理
高斯混合模型(Gaussian mixture model,GMM),常用于聚类,它与K-means算法有个相同之处在于,都需要指定 [公式] 值。它使用EM算法来求解,有时只能收敛于局部最优。
📌GMM不仅仅可以用于聚类,还可以用于概率密度的估计,也可以用于生成新的样本。
-
API
3、基于最近邻KNN实现背景提取
-
原理
📌 KNN算法的原则就是寻找K个与样本最相似的数据,这K个数据大多数属于哪个类别则样本归属到哪个类别中。
-
API
三、实例
引用Opencv官方Example里的视频 vtest.mp4 来实现背景消除和ROI行人提取。
步骤:
- 初始化背景建模对象GMM
- 读取视频一帧
- 使用背景建模消除生成mask
- 对mask进行轮廓分析提取ROI
- 绘制ROI对象
void bg() {
//1 读取视频
VideoCapture cap = VideoCapture("D:/test/vtest.avi");
//1.1 获取视频的总帧数
double framesNum=cap.get(CAP_PROP_FRAME_COUNT);
cout << " frameNum=" << framesNum << endl;
//2. 背景消除
//采用高斯混合模型分离算法MOG2和KNN算法进行前景分离
Ptr<BackgroundSubtractor> bgMOG2;
Ptr<BackgroundSubtractor> bgKNN;
//2.1 高斯混合模型分离算法MOG2
bgMOG2=createBackgroundSubtractorMOG2(500, 100,false);
//2.2 KNN算法
bgKNN = createBackgroundSubtractorKNN(500, 400, false);
//3. 进行帧读取和处理
Mat maskMOG2,maskKNN,ret,frame;
//3.1 k指从键盘输出的值
int k = 0;
//3.2 flag计算当前读取的帧数
double flag = 0;
//3.3 循环处理每一帧图片,直到按下ESC或者当前视频读取完停止循环
while (char(k) != 'q' && (char)k!= 27)
{
//3.4 读取每一帧
cap.read(frame);
//克隆读取到的图片
Mat frameKNN = frame.clone();
//显示读取的视频
imshow("frame", frame);
//4 采用基于混合高斯的算法计算前景蒙版
bgMOG2->apply(frame, maskMOG2);
//采用基于KNN的算法计算前景蒙版
bgKNN->apply(frame, maskKNN);
//4.1 进行形态学的开操作
Mat kernel=getStructuringElement(MORPH_RECT, Size(3, 3));
morphologyEx(maskMOG2, maskMOG2, MORPH_OPEN, kernel);
morphologyEx(maskKNN, maskKNN, MORPH_OPEN, kernel);
//4.2 显示混合高斯算法的前景蒙版并保存每一帧
imshow("maskMOG2", maskMOG2);
String name11= "D:/test/maskMOG2/" + std::to_string((int)flag) + ".png";
cout << "name11: " << name11 << " ";
imwrite(name11, maskMOG2);
name11 = "";
//4.3 显示KNN算法的前景蒙版并保存每一帧
imshow("maskKNN", maskKNN);
String name22 = "D:/test/maskKNN/" + std::to_string((int)flag) + ".jpg";
cout << "name22: " << name22 << " ";
imwrite(name22, maskKNN);
name22 = "";
//5. 分离出前景
Mat img1, img2, img3,qjKNN, bg;//==mask
//5.1 反转二值图像
bitwise_not(maskKNN, img1);
//5.2 按位与操作
bitwise_and(frame, frame, img2, maskKNN);
//5.3 设置背景为全白色
bg=Mat::zeros(frame.size(), frame.type());
bg.setTo(Scalar(255, 255, 255));
//5.4 按位或操作
bitwise_or(img2, bg, img3, img1);
//5.5 加法操作
add(img3, img2,qjKNN);
//5.6 显示前景图像并保存每一帧
imshow("img3", qjKNN);
String name33 = "D:/test/people/" + std::to_string((int)flag) + ".png";
cout << "name33: " << name33 << " ";
imwrite(name33, img3);
name33 = "";
//6. MOG2的轮廓提取
// 保存每一帧轮廓
vector<vector<Point>> contoursMOG2,contoursKNN;
//6.1 对MOG2的前景蒙版进行轮廓提取
findContours(maskMOG2, contoursMOG2, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//6.2 对KNN的前景蒙版进行轮廓提取
findContours(maskKNN, contoursKNN, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//7. 循环遍历MOG2的前景蒙版轮廓
for (int i = 0; i < contoursMOG2.size(); i++)
{
//7.1 计算每一个轮廓面积
double area=contourArea(contoursMOG2[i]);
//7.2 轮廓面积过小的直接略过
if (area<150)
{
continue;
}
//7.3 计算轮廓最小的外接矩形
RotatedRect rect=minAreaRect(contoursMOG2[i]);
//7.4 每一轮廓拟合成椭圆并画到读取的帧上
ellipse(frame, rect, Scalar(0, 255, 0), 2);
//7.5 画出轮廓中椭圆的圆心并画到读取的帧上
circle(frame, rect.center,2,Scalar(0,0,255),2);
}
//7.6 保存每一帧轮廓处理后的帧
String name = "D:/test/MOG2/" +std::to_string((int)flag)+".png" ;
cout << "name: " << name << " ";
imwrite(name, frame);
name = "";
imshow("ResultMOG2", frame);
//7. KNN的轮廓提取,同上
for (int i = 0; i < contoursKNN.size(); i++)
{
double area = contourArea(contoursKNN[i]);
if (area < 150)
{
continue;
}
RotatedRect rect = minAreaRect(contoursKNN[i]);
ellipse(frameKNN, rect, Scalar(0, 255, 0), 2);
circle(frameKNN, rect.center, 2, Scalar(0, 0, 255), 2);
}
String name2 = "D:/test/KNN/" + std::to_string((int)flag) + ".png";
cout << "name2: " << name2 << " ";
imwrite(name2, frameKNN);
name2 = "";
imshow("ResultKNN", frameKNN);
//8. 处理完一帧后读取下一帧
flag+=1;
//9. 若读取到视频最后一帧,停止循环
if (flag==framesNum)
{
break;
}
k = waitKey(30);
}
}
📌采用createBackgroundSubtractorMOG2生成的前15帧gif动画:
中间15帧:
134帧前景的gif图:
📌采用 createBackgroundSubtractorKNN生成的前15帧gif动画:
中间15帧:
134帧前景图片:
📌提取knn下的210张前景图片组成的gif:
学习:
OpenCV–0016:图像ROI与ROI操作
OpenCV视频篇——背景/前景分割类
OpenCV图像处理- 视频背景消除与前景ROI提取
高斯混合模型(GMM)算法理解及代码实现