前面我们看到通过光流法进行运动物体跟踪的实际例子,其实现的基本原理就是:
一、获取前一帧,然后转换为灰度图,利用cvGoodFeaturesToTrack 函数寻找这一帧视频流的强角点
二、获取当前帧,然后转换为灰度图,利用LK金字塔算法 cvCalcOpticalFlowPyrLK 函数结合第一帧寻找到的角点A,寻找当前帧的角点B
三、画出角点和运动轨迹
背景差分法进行运动物体检测的基本原理就是:
一、取前一帧的视频流作为运动背景
二、将当前帧的视频流中的每个像素与前一帧的每个像素做差,得出每个像素点前后两帧的值的差
三、判断每个像素点的前后两帧的差值是否大于某个阈值,是则判断为运动
关于 cvGoodFeaturesToTrack 的原理,不清楚的可以参考博客:
OpenCv目标跟踪_cvGoodFeaturesToTrack()寻找角点
其基本原理也就是,强角点所在的位置在图像中一般在两个正交方向上都有明显的倒数,该点在图像中我们认为是独一无二的。
而差分背景法的基本原理,我们从另一个角度上来看,其实也是寻找角点的过程,它在前后两帧图像的每个像素作对比的过程中,过滤掉了像素值变化小的点,变化大的点被认为是运动的,其实这些运动的点从某个意义上也就是我们想要找的角点。
接下来我们来实现这样的思路:
先运用差分背景法,找出前一帧中我们认为是运动的角点,将这些角点保存起来,再结合这些找到的角点运用LK金字塔算法计算出光流,从而实现基于背景差分法的运动物体检测跟踪。
看代码:
/*
* Description : Tracking Moving object based on background subtraction
* Author : Liulongpo
* Date : 2015年3月9日21:24:56
*
*/
#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace std;
bool acceptTrackedPoint(int i);
int const MAX_CORNERS = 200;
CvPoint2D32f * cornerA;
CvPoint2D32f * cornerB;
int main (int argc, char **argv)
{
CvCapture* capture = 0;
//capture = cvCaptureFromCAM( CV_CAP_ANY ); //get frame
capture = cvCaptureFromFile( "F://BaiduYunDownload//bike.avi" );
IplImage *pre_frame; //the previous frame
IplImage *cur_frame; //the current frame
IplImage *dst_img; //the result
IplImage *cur_img;
IplImage *pre_img;
cornerA = new CvPoint2D32f[ MAX_CORNERS];
cornerB = new CvPoint2D32f[ MAX_CORNERS];
char *features_found = new char[MAX_CORNERS];
float *features_error = new float[MAX_CORNERS];
CvTermCriteria criteria;
criteria = cvTermCriteria (CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 64, 0.01);
// 获取前一帧
pre_frame = cvQueryFrame(capture);
while(1)
{
// 获取当前帧
cur_frame = cvQueryFrame(capture);
if (!cur_frame)
{
cout<<"No frame is captured ! \n";
break;
}
int i,j;
int point_number = 0;
int rows, cols;
int countn = MAX_CORNERS;
CvSize img_sz = cvGetSize(cur_frame);
pre_img = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvCvtColor(pre_frame,pre_img, CV_RGB2GRAY);
cur_img = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvCvtColor(cur_frame,cur_img , CV_RGB2GRAY);
dst_img = (IplImage *)cvClone(cur_frame);
IplImage *move_img = cvCreateImage(img_sz, IPL_DEPTH_8U, 1);
cvZero(move_img);
//cvAbsDiff(src_img1, src_img2,move_img);
cols = cur_frame->width;
rows = cur_frame->height;
for (i = 0; i <cols; i++)
{
for (j = 0; j<rows; j++)
{
// 如果前后两帧的同一个像素点的值之差大于阈值,则认为该像素运动了
double a = abs(cvGet2D(pre_img, j, i).val[0]-cvGet2D(cur_img, j, i).val[0]);
CvScalar b = cvScalar(a, 0, 0,0);
// 设置灰度图像
cvSet2D(move_img, j, i,b);
if (a>40)
{
if (point_number<MAX_CORNERS-1)
{
cornerA[++point_number].x = i;
cornerA[point_number].y = j;
}
}
}
}
cvNamedWindow("moving object", 0);
cvShowImage("moving object", move_img);
CvSize Pyrsize = cvSize(pre_img->width +8, pre_img->height/3);
IplImage * pyrA = cvCreateImage(Pyrsize, IPL_DEPTH_32F, 1);
IplImage * pyrB = cvCreateImage(Pyrsize, IPL_DEPTH_32F, 1);
cvCalcOpticalFlowPyrLK(pre_img,cur_img,pyrA,pyrB,cornerA,cornerB,countn,
cvSize(10, 10),3,features_found,features_error,criteria,0);
for (i = 0; i < countn; i++)
{
if (features_found[i] && acceptTrackedPoint(i))
{
cvLine (dst_img, cvPointFrom32f (cornerA[i]), cvPointFrom32f (cornerB[i]), CV_RGB (0, 255, 0), 1, CV_AA, 0);
//cvCircle(dst_img,cvPointFrom32f (cornerB[i]), 3, CV_RGB (255, 0, 0), -1);
}
// 交换角点数据
cornerA[i] = cornerB[i];
}
// 当前帧交换到前一帧
pre_frame = (IplImage *)cvClone(cur_frame);
cvNamedWindow ("ImagePyrLK", 0);
cvShowImage ("ImagePyrLK", dst_img);
cvWaitKey (1);
cvReleaseImage (&dst_img);
cvReleaseImage(&pyrA);
cvReleaseImage(&pyrB);
cvReleaseImage(&move_img);
}
cvDestroyWindow("moving object");
cvDestroyWindow ("ImagePyrLK");
cvReleaseImage (&pre_frame);
cvReleaseImage (&cur_frame);
cvReleaseImage (&pre_img);
cvReleaseImage (&cur_img);
return 0;
}
// 通过设定之前点与当前点的距离来判断特征点是否可以获取
bool acceptTrackedPoint(int i)
{
//cout<<abs(cornerA[i].x - cornerB[i].x)<< " ";
return ((abs(cornerA[i].x - cornerB[i].x) + abs(cornerA[i].y - cornerB[i].y)) > 3);
}
效果如下:
原文地址:http://blog.csdn.net/llp1992/article/details/44160695