简述:在一些工业现场及其他环境,使用深度学习的方法进行图像处理是不可行的(原因有成本问题等)。也正因如此尽管笔者偏向于python编程,但这次主要做的是C++环境下的行人检测。这里主要采用的是背景板减法,即opencv中自带的BackgroundSubtractorMOG2函数。该函数基于自适应混合高斯背景建模,具有一定的抗光照干扰的能力。基本配置是VS2013+opencv3.0.0 。
背景板法基本原理:利用当前帧和参考帧之间差进行运动物体的检测,如果加以约束条件,这些运动的物体就是我们感兴趣的目标。对于背景而言,opencv的MOG2函数主要是利用Adaptive GMM模型,即自适应混合高斯模型来模拟背景。GMM的模型详解可以参考博文:点击打开链接 。简单来说就是利用EM算法来估计GMM的参数,再用这些参数来求最大似然函数。根据Zoran的说法,其算法要比opencv中另一种方法GMG快大约40%。
MOG2基本使用方法:
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:方差阈值,用于已经存在的匹配的模型,如果不存在则新建一个。
行人检测思路:主要是采用人体特定的长宽比进行计算,筛选近似于人体长宽比的物体进行画框标注,针对误判情况,添加必须大于一定帧数阈值的方式予以消除。
算法实例:
#include "stdafx.h"
#include <opencv2\imgproc\imgproc.hpp>
#include <windows.h>
#include <opencv2/opencv.hpp>
#include <cmath>
#include <iostream>
//#include "DetectPackage.h"
#include <cmath>
using namespace std;
using namespace cv;
int main() {
VideoCapture cap("G://folder/test.mp4");
if (!cap.isOpened()) {
cout << "video not exist!" << endl;
return -1;
}
long FRAMECNT = cap.get(CV_CAP_PROP_FRAME_COUNT);
Mat frame, mask, maskCp;
vector<vector<Point>> cnts;
Rect maxRect;
const double RECT_HW_RATIO = 1.5; // 人体长宽比阈值
const double RECT_AREA_RATIO = 0.008; // 人体占整个图像最小比例阈值
const double RECT_AREA_RATIO2 = 0.2; // 人体占整体图像最大比例阈值
Ptr<BackgroundSubtractorMOG2> bgsubtractor = createBackgroundSubtractorMOG2();
bgsubtractor->setHistory(20);
bgsubtractor->setVarThreshold(100);
bgsubtractor->setDetectShadows(true);
bool hasPeople = false; // 是否有人
int count = 0; // 帧数
int hasPeopleFrameCnt = 0; // 每K帧统计到的有人帧数
int spaceFrames = 0; // 每隔125帧统计一次
const int SPACE_FRAME = 125;
while (++count < FRAMECNT - 10) {
cap >> frame;
resize(frame, frame, Size(frame.cols / 4, frame.rows / 4));
// 背景更新
bgsubtractor->apply(frame, mask, 0.002);
// 中值滤波
medianBlur(mask, mask, 3);
// 阈值分割,去阴影
threshold(mask, mask, 200, 255, CV_THRESH_BINARY);
// 找轮廓
maskCp = mask.clone();
findContours(maskCp, cnts, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
vector<Point> maxCnt;
for (int i = 0; i < cnts.size(); ++i) {
maxCnt = maxCnt.size() > cnts[i].size() ? maxCnt : cnts[i];
}
// 画最大外接矩形
if (maxCnt.size() > 0) {
maxRect = boundingRect(maxCnt);
double rectAreaRatio = (double)maxRect.area() / (frame.cols * frame.rows);
if ((double)maxRect.height / maxRect.width > RECT_HW_RATIO && rectAreaRatio > RECT_AREA_RATIO &&
rectAreaRatio < RECT_AREA_RATIO2) {
rectangle(frame, maxRect.tl(), maxRect.br(), Scalar(0, 255, 0), 2);
++hasPeopleFrameCnt;
}
}
++spaceFrames;
if (spaceFrames >= SPACE_FRAME) {
if (hasPeopleFrameCnt > SPACE_FRAME / 8) {
hasPeople = true;
cout << count << ":有人" << endl;
}
else {
hasPeople = false;
cout << count << ":无人" << endl;
}
hasPeopleFrameCnt = 0;
spaceFrames = 0;
}
imshow("frame", frame);
imshow("mask", mask);
if (waitKey(10) == 27) {
break;
}
}
return 0;
};