主要介绍四种运动目标检测的算法代码,每段代码博主实测可运行。当前主流的混合高斯背景模型,VIBE算法代码转载自他处。另外GMG算法,KNN算法在朱伟的书中也有讲,opencv3.0中,有专门的背景模型类BackgroundSubtractor。详情请下载朱伟《图像处理编程实例》源代码–下载资源
原理在另一篇文章中有些《运动目标检测》
1、帧间差分法求前景
帧间差分主要思想为两帧图像灰度化后相减得绝对值函数矩阵,然后二值化(注意阈值选择),得到前景图像。
#include "stdafx.h"
#include"opencv2\opencv.hpp"
#include
#include
#include
using namespace cv;
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
Mat img_src1, img_src2, gray1, gray2, gray_diff;
int thvalue = 30;
img_src1 = imread("F:\\毕业论文相关\\机场视频\\图片5帧\\density_img_5.jpg");
img_src2 = imread("F:\\毕业论文相关\\机场视频\\图片5帧\\density_img_30.jpg");
if (!img_src1.data || !img_src2.data)
return -1;
cvtColor(img_src1, gray1, CV_RGB2GRAY);
cvtColor(img_src2, gray2, CV_RGB2GRAY);
Mat img_dst(img_src1.rows, img_src1.cols, CV_8UC1);
IplImage* Tsrcimage = &IplImage(gray1);//取指针
IplImage* Tdstimage = &IplImage(gray2);
IplImage* Tdif = &IplImage(img_dst);
cvAbsDiff(Tsrcimage, Tdstimage, Tdif);//背景相减得灰度图
cvThreshold(Tdif, Tdif, thvalue, 255, CV_THRESH_BINARY); //如果 src(x,y)>threshold ,dst(x,y) = max_value; 否则,dst(x,y)=0;
cvShowImage("foreground", Tdif);
//imshow("foreground", img_dst);
waitKey();
return 0;
}
2、三帧差分法求前景
第二帧减第一帧,第三帧减第二帧,结果相加,得到三帧差分图像。
#include "stdafx.h"
#include"opencv2\opencv.hpp"
#include
#include
#include
using namespace cv;
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
Mat img_src1, img_src2, img_src3;
Mat gray1, gray2, gray3;
int thvalue = 30;
img_src1 = imread("F:\\毕业论文相关\\机场视频\\图片5帧\\density_img_5.jpg");
img_src2 = imread("F:\\毕业论文相关\\机场视频\\图片5帧\\density_img_40.jpg");
img_src3 = imread("F:\\毕业论文相关\\机场视频\\图片5帧\\density_img_65.jpg");
if (!img_src1.data || !img_src2.data)
return -1;
cvtColor(img_src1, gray1, CV_RGB2GRAY);
cvtColor(img_src2, gray2, CV_RGB2GRAY);
cvtColor(img_src3, gray3, CV_RGB2GRAY);
Mat result1(img_src1.rows, img_src1.cols, CV_8UC1);
Mat result2(img_src1.rows, img_src1.cols, CV_8UC1);
Mat gray_diff(img_src1.rows, img_src1.cols, CV_8UC1);
IplImage* Tgray1 = &IplImage(gray1);//取指针
IplImage* Tgray2 = &IplImage(gray2);
IplImage* Tgray3 = &IplImage(gray3);
IplImage* Tresult1 = &IplImage(result1);
IplImage* Tresult2 = &IplImage(result2);
IplImage* Tdif = &IplImage(gray_diff);
cvAbsDiff(Tgray1, Tgray2, Tresult1);//背景相减得灰度图
cvAbsDiff(Tgray2, Tgray3, Tresult2);//背景相减得灰度图
cvThreshold(Tresult1, Tresult1, thvalue, 255, CV_THRESH_BINARY); //如果 src(x,y)>threshold ,dst(x,y) = max_value; 否则,dst(x,y)=0;
cvThreshold(Tresult2, Tresult2, thvalue, 255, CV_THRESH_BINARY);
cvAdd(Tresult1, Tresult2,Tdif);
cvShowImage("foreground", Tdif);
cvErode(Tdif, Tdif); //腐蚀
cvShowImage("腐蚀后", Tdif);
cvDilate(Tdif, Tdif); //膨胀
cvShowImage("膨胀后", Tdif);
//imshow("foreground", img_dst);
waitKey();
return 0;
}
3、混合高斯背景模型实现
// 功能:代码 9-5 高斯混合背景建模
// 作者:朱伟 [email protected]
// 来源:《OpenCV图像处理编程实例》
// 博客:http://blog.csdn.net/zhuwei1988
// 更新:2016-8-1
// 说明:版权所有,引用或摘录请联系作者,并按照上面格式注明出处,谢谢。//
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include
#include
#include
#include
using namespace cv;
using namespace std;
Mat frame;
Mat fgMaskMOG2;
Ptr pMOG2;
int keyboard;
void processVideo(string videoFilename)
{
// 视频获取
VideoCapture capture(videoFilename);
if(!capture.isOpened())
{
// 输出视频文件打开错误信息
cerr << "Unable to open video file: " << videoFilename << endl;
exit(EXIT_FAILURE);
}
// 按下q键和esc退出
while( (char)keyboard != 'q' && (char)keyboard != 27 )
{
// 读取当前帧
if(!capture.read(frame))
{
cerr << "Unable to read next frame." << endl;
cerr << "Exiting..." << endl;
exit(EXIT_FAILURE);
}
// 图像尺寸缩小
cv::resize(frame, frame,cv::Size(), 0.25,0.25);
// 背景模型生成
pMOG2->apply(frame, fgMaskMOG2);
// 输出当前帧号
stringstream ss;
rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
cv::Scalar(255,255,255), -1);
ss << capture.get(CAP_PROP_POS_FRAMES);
string frameNumberString = ss.str();
// 左上角显示帧号
putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
// 输出结果
imshow("Frame", frame);
imshow("FG Mask MOG 2", fgMaskMOG2);
keyboard = waitKey(30);
}
capture.release();
}
int main(int argc, char* argv[])
{
// 创建背景建模类
pMOG2 = createBackgroundSubtractorMOG2();
string inputPath = "F:\\毕业论文相关\\机场视频\\机场.avi";
processVideo(inputPath);
return 0;
}
4、vibe算法
(算法程序出处找不到了,如果恰好作者看到,留言一下我给加上,实在抱歉。)
主函数代码如下
#include "stdafx.h"
#include
#include "highgui.h"
#include "hanshu.h"
#include
using namespace std;
using namespace cv;
int nFrmNum = 0;//记录帧数
int Width;//记录帧的宽度
int Height;//记录帧的高度
int FrameRate;//记录视频帧率
int main(int argc, char* argv[])
{
IplImage* pFrame = NULL; CvMat* pFrameMat = NULL;//pFrame对象
IplImage* pAfter = NULL; CvMat* pAfterMat = NULL;//保存pFrame对应的灰度图像
IplImage* segMap = NULL; CvMat* segMat = NULL;//保存处理后的信息
//要读取的视频文件和保存的视频文件路径
char* openfile = "F:\\毕业论文相关\\机场视频\\密度人群.ts";
char* outfile = "E:\\View004.avi";//检测结果输出位置
char filename[100];//保存的图像位置和名称
//打开视频文件
CvCapture* pCapture = cvCreateFileCapture(openfile);
if (pCapture == NULL) {
cout << "video file open error!" << endl;
return -1;
}
//获取视频相关信息,帧率和大小
double fps = cvGetCaptureProperty(pCapture, CV_CAP_PROP_FPS);
CvSize size = cvSize((int)cvGetCaptureProperty(pCapture, CV_CAP_PROP_FRAME_WIDTH) * 2,
(int)cvGetCaptureProperty(pCapture, CV_CAP_PROP_FRAME_HEIGHT));
//创建输出视频文件
CvVideoWriter* Save_result = NULL;
Save_result = cvCreateVideoWriter(outfile, CV_FOURCC('X', 'V', 'I', 'D'), fps, size, 1);
IplImage* dstImg = cvCreateImage(size, IPL_DEPTH_8U, 3);//创建要保存的图像
//创建窗口
cvNamedWindow("video", CV_WINDOW_AUTOSIZE);
//创建一个随机数生成器
RNG rng(0xFFFFFFFF);
//定义字体
CvFont font;
cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX_SMALL, 1, 1, 0, 1, 8);
//逐帧读取视频并进行处理 pFrame为取出原始帧
while (pFrame = cvQueryFrame(pCapture)){
nFrmNum++;
//如果是第一帧,申请内存并进行初始化
if (nFrmNum == 1){
segMap = cvCreateImage(cvSize(pFrame->width, pFrame->height),
IPL_DEPTH_8U, 1); //保存处理后的信息
segMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);//保存处理后的信息
pAfter = cvCreateImage(cvSize(pFrame->width, pFrame->height),
IPL_DEPTH_8U, 1);//保存pFrame对应的灰度图像
pAfterMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);//保存pFrame对应的灰度图像
//转化成单通道图像再处理
cvCvtColor(pFrame, pAfter, CV_BGR2GRAY);
cvConvert(pAfter, pAfterMat);
//
Initialize(pAfterMat, rng);//以一个随机数生成器初始化灰度图像
}
else {
if (nFrmNum%30==0)//此处为每30帧读取一次
{
cvCvtColor(pFrame, pAfter, CV_BGR2GRAY);//RGB空间转换为RAY空间
cvConvert(pAfter, pAfterMat);//用于图像和矩阵之间的相互转换
update(pAfterMat, segMat, rng, nFrmNum);//更新背景
cvConvert(segMat, segMap);//用于图像和矩阵之间的相互转换
//载入原图像到目标图像
cvSetImageROI(dstImg, cvRect(0, 0, pFrame->width, pFrame->height));
cvCopy(pFrame, dstImg);
cvResetImageROI(dstImg);
//将segMap转换成三通道图像存在pFrame中
cvCvtColor(segMap, pFrame, CV_GRAY2BGR);
//载入检测后的图像到目标图像
cvSetImageROI(dstImg, cvRect(pFrame->width, 0, pFrame->width * 2, pFrame->height));
cvCopy(pFrame, dstImg);
cvResetImageROI(dstImg);
//显示提示文字
cvPutText(dstImg, "Input Video", cvPoint(0, pFrame->height - 5), &font, CV_RGB(255, 0, 0));
cvPutText(dstImg, "Vibe Segmentation", cvPoint(pFrame->width, pFrame->height - 5), &font, CV_RGB(255, 0, 0));
//保存视频和输出
cvWriteFrame(Save_result, dstImg);
/*输出图片
if(nFrmNum<11)
sprintf(filename,"E:\\Result\\Vibe\\AVSS_PV_Easy_Divx_Xvid\\000%d_Vibe.jpg",nFrmNum-1);
else if(nFrmNum<101)
sprintf(filename,"E:\\Result\\Vibe\\AVSS_PV_Easy_Divx_Xvid\\00%d_Vibe.jpg",nFrmNum-1);
else if(nFrmNum<1001)
sprintf(filename,"E:\\Result\\Vibe\\AVSS_PV_Easy_Divx_Xvid\\0%d_Vibe.jpg",nFrmNum-1);
else if(nFrmNum<10001)
sprintf(filename,"E:\\Result\\Vibe\\AVSS_PV_Easy_Divx_Xvid\\%d_Vibe.jpg",nFrmNum-1);
cvSaveImage(filename,dstImg);
*/
cvShowImage("video", dstImg);
if (cvWaitKey(5) >= 0) break;
}
}
}
cvReleaseImage(&pFrame); cvReleaseMat(&pFrameMat);
cvReleaseImage(&pAfter); cvReleaseMat(&pAfterMat);
cvReleaseImage(&segMap); cvReleaseMat(&segMat);
cvReleaseVideoWriter(&Save_result);
cvReleaseImage(&dstImg);
cvDestroyWindow("video");
return 0;
}
类文件hanshu.h
#include "stdafx.h"
#include
#include "highgui.h"
#include
using namespace std;
using namespace cv;
#define defaultNbSamples 20 //每个像素点的样本个数
#define defaultReqMatches 2 //#min指数
#define defaultRadius 20 //Sqthere半径
#define defaultSubsamplingFactor 16 //子采样概率
#define background 0 //背景像素
#define foreground 255 //前景像素
void Initialize(CvMat* pFrameMat,RNG rng);//初始化
void update(CvMat* pFrameMat,CvMat* segMat,RNG rng,int nFrmNum);//更新
类文件hanshu.cpp
#include "stdafx.h"
#include "hanshu.h"
#include
#include "highgui.h"
#include
#include
using namespace std;
using namespace cv;
static int c_xoff[9] = {-1, 0, 1, -1, 1, -1, 0, 1, 0};//x的邻居点
static int c_yoff[9] = {-1, 0, 1, -1, 1, -1, 0, 1, 0};//y的邻居点
float samples[1024][1024][defaultNbSamples+1];//保存每个像素点的样本值
//初始化
void Initialize(CvMat* pFrameMat,RNG rng){
//记录随机生成的 行(r) 和 列(c)
int rand,r,c;
//对每个像素样本进行初始化
for(int y=0;yrows;y++){//Height
for(int x=0;xcols;x++){//Width
for(int k=0;k//随机获取像素样本值
rand=rng.uniform( 0, 9 );//b如果是真值,返回a,否则返回(int)(next() % (b - a) + a);
r=y+c_yoff[rand]; if(r<0) r=0; if(r>=pFrameMat->rows) r=pFrameMat->rows-1; //行
c=x+c_xoff[rand]; if(c<0) c=0; if(c>=pFrameMat->cols) c=pFrameMat->cols-1; //列
//存储像素样本值
samples[y][x][k]=CV_MAT_ELEM(*pFrameMat,float,r,c);
}
samples[y][x][defaultNbSamples]=0;
}
}
}
//更新函数
void update(CvMat* pFrameMat,CvMat* segMat,RNG rng,int nFrmNum){
for(int y=0;yrows;y++){ //Height
for(int x=0;xcols;x++){ //Width
//用于判断一个点是否是背景点,index记录已比较的样本个数,count表示匹配的样本个数
int count=0,index=0;float dist=0;
//
while((count*pFrameMat,float,y,x)-samples[y][x][index];
if(dist<0) dist=-dist;
if(distif(count>=defaultReqMatches){
//判断为背景像素,只有背景点才能被用来传播和更新存储样本值
samples[y][x][defaultNbSamples]=0;
*((float *)CV_MAT_ELEM_PTR(*segMat,y,x))=background;
int rand=rng.uniform(0,defaultSubsamplingFactor);
if(rand==0){
rand=rng.uniform(0,defaultNbSamples);
samples[y][x][rand]=CV_MAT_ELEM(*pFrameMat,float,y,x);
}
rand=rng.uniform(0,defaultSubsamplingFactor);
if(rand==0){
int xN,yN;
rand=rng.uniform(0,9);yN=y+c_yoff[rand];if(yN<0) yN=0; if(yN>=pFrameMat->rows) yN=pFrameMat->rows-1;
rand=rng.uniform(0,9);xN=x+c_xoff[rand];if(xN<0) xN=0; if(xN>=pFrameMat->cols) xN=pFrameMat->cols-1;
rand=rng.uniform(0,defaultNbSamples);
samples[yN][xN][rand]=CV_MAT_ELEM(*pFrameMat,float,y,x);
}
}
else {
//判断为前景像素
*((float *)CV_MAT_ELEM_PTR(*segMat,y,x))=foreground;
samples[y][x][defaultNbSamples]++;
if(samples[y][x][defaultNbSamples]>50){
int rand=rng.uniform(0,defaultNbSamples);
if(rand==0){
rand=rng.uniform(0,defaultNbSamples);
samples[y][x][rand]=CV_MAT_ELEM(*pFrameMat,float,y,x);
}
}
}
}
}
}