#include "cv.h"
#include "highgui.h"
#include "stdlib.h"
#include "malloc.h"
#include "cxcore.h"
#include "assert.h"
#include <time.h>
#include <math.h>
#include <ctype.h>
#include <stdio.h>
#include <iostream>
using namespace std;
// various tracking parameters (in seconds)
const double MHI_DURATION = 1;
const double MAX_TIME_DELTA = 0.5;
const double MIN_TIME_DELTA = 0.05;
// number of cyclic frame buffer used for motion detection
// (should, probably, depend on FPS)
//用于运动检测的循环帧数
const int N = 4;
// ring image buffer
IplImage **buf = 0;
int last = 0;
// temporary images
IplImage *mhi = 0; // MHI
IplImage *orient = 0; // orientation
IplImage *mask = 0; // valid orientation mask
IplImage *segmask = 0; // motion segmentation map
CvMemStorage* storage = 0; // temporary storage
// parameters:
// img - input video frame
// dst - resultant motion picture
// args - optional parameters
void update_mhi(IplImage* img, IplImage* dst, int diff_threshold)
{
double timestamp = (double)clock() / CLOCKS_PER_SEC; // get current time in seconds
CvSize size = cvSize(img->width, img->height); // get current frame size
int i, idx1 = last, idx2;
IplImage* silh;
CvSeq* seq;
CvRect comp_rect;
double count;
double angle;
CvPoint center;
double magnitude;
CvScalar color;
// allocate images at the beginning or 为图像分配初始空间
// reallocate them if the frame size is changed 当帧的大小改变时,重新分配内存空间
if (!mhi || mhi->width != size.width || mhi->height != size.height)
{
if (buf == 0)
{
buf = (IplImage**)malloc(N*sizeof(buf[0]));
memset(buf, 0, N*sizeof(buf[0]));//把申请到的内存空间用0初始化
}
for (i = 0; i < N; i++) {
cvReleaseImage(&buf[i]);
buf[i] = cvCreateImage(size, IPL_DEPTH_8U, 1);
cvZero(buf[i]);
}
cvReleaseImage(&mhi);
cvReleaseImage(&orient);
cvReleaseImage(&segmask);
cvReleaseImage(&mask);
mhi = cvCreateImage(size, IPL_DEPTH_32F, 1);
cvZero(mhi); // clear MHI at the beginning
orient = cvCreateImage(size, IPL_DEPTH_32F, 1);
segmask = cvCreateImage(size, IPL_DEPTH_32F, 1);
mask = cvCreateImage(size, IPL_DEPTH_8U, 1);
}
cvCvtColor(img, buf[last], CV_BGR2GRAY); // convert frame to grayscale 转换为灰度图
idx2 = (last + 1) % N; // index of (last - (N-1))th frame
last = idx2;
silh = buf[idx2];
cvAbsDiff(buf[idx1], buf[idx2], silh); // get difference between frames相邻两帧之差
cvThreshold(silh, silh, diff_threshold, 1, CV_THRESH_BINARY); // and threshold it
cvUpdateMotionHistory(silh, mhi, timestamp, MHI_DURATION); // update MHI
// convert MHI to blue 8u image
cvCvtScale(mhi, mask, 255. / MHI_DURATION,
(MHI_DURATION - timestamp)*255. / MHI_DURATION);
cvZero(dst);
cvCvtPlaneToPix(mask, 0, 0, 0, dst);
// calculate motion gradient orientation and valid orientation mask计算运动的梯度方向以及正确的方向掩码
cvCalcMotionGradient(mhi, mask, orient, MAX_TIME_DELTA, MIN_TIME_DELTA, 3);
if (!storage)
storage = cvCreateMemStorage(0);
else
cvClearMemStorage(storage);
// segment motion: get sequence of motion components运动分割:获取运动组成成分的序列
// segmask is marked motion components map. It is not used further
seq = cvSegmentMotion(mhi, segmask, storage, timestamp, MAX_TIME_DELTA);
// iterate through the motion components,迭代运动的组成成分
// One more iteration (i == -1) corresponds to the whole image (global motion)
for (i = -1; i < seq->total; i++)
{
if (i < 0)
{ // case of the whole image
comp_rect = cvRect(0, 0, size.width, size.height);
color = CV_RGB(255, 255, 255);
magnitude = 100;
}
else
{ // i-th motion component
comp_rect = ((CvConnectedComp*)cvGetSeqElem(seq, i))->rect;
if (comp_rect.width + comp_rect.height < 100) // reject very small components
continue;
color = CV_RGB(255, 0, 0);
magnitude = 30;
}
// select component ROI
cvSetImageROI(silh, comp_rect);
cvSetImageROI(mhi, comp_rect);
cvSetImageROI(orient, comp_rect);
cvSetImageROI(mask, comp_rect);
// calculate orientation在选择区域内计算运动方向
angle = cvCalcGlobalOrientation(orient, mask, mhi, timestamp, MHI_DURATION);
angle = 360.0 - angle; // adjust for images with top-left origin
count = cvNorm(silh, 0, CV_L1, 0); // calculate number of points within silhouette ROI
cvResetImageROI(mhi);
cvResetImageROI(orient);
cvResetImageROI(mask);
cvResetImageROI(silh);
// check for the case of little motion
if (count < comp_rect.width*comp_rect.height * 0.05)
continue;
//// draw a clock with arrow indicating the direction画一个“钟表”指向运动的方向
//center = cvPoint((comp_rect.x + comp_rect.width / 2),
// (comp_rect.y + comp_rect.height / 2));
//cvCircle(dst, center, cvRound(magnitude*1.2), color, 3, CV_AA, 0);
//cvLine(dst, center, cvPoint(cvRound(center.x + magnitude*cos(angle*CV_PI / 180)),
// cvRound(center.y - magnitude*sin(angle*CV_PI / 180))), color, 3, CV_AA, 0);
}
}
int main(int argc, char** argv)
{
IplImage* motion = 0;
CvCapture* capture = 0;
capture = cvCaptureFromFile("E:\Videos\\daria_walk.avi");
if (capture)
{
cvNamedWindow("Motion", 1);
for (;;)
{
IplImage* image;
if (!cvGrabFrame(capture))
break;
//Decodes and returns the grabbed video frame.
//C++: bool VideoCapture::retrieve(Mat& image, int channel=0)
// IplImage* cvRetrieveFrame(CvCapture* capture, int streamIdx=0 )
image = cvRetrieveFrame(capture);
if (image)
{
if (!motion)
{
//Creates an image header and allocates the image data.
//C: IplImage* cvCreateImage(CvSize size, int depth, int channels)
motion = cvCreateImage(cvSize(image->width, image->height), 8, 3);
//Clears the array.
//C: void cvSetZero(CvArr* arr)
cvZero(motion);
motion->origin = image->origin;
}
}
update_mhi(image, motion, 30);
cvShowImage("Motion", motion);
if (cvWaitKey(10) >= 0)
break;
}
cvReleaseCapture(&capture);
cvDestroyWindow("Motion");
}
return 0;
}