#include<opencv2/opencv.hpp>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
//不同的跟踪参数
const double MHI_DURATION = 0.5;
const double MAX_TIME_DELTA = 0.5;
const double MIN_TIME_DELTA = 0.05;
// 用于运动检测的循环帧数,与机器速度及FPS设置有关
const int N = 2;
IplImage **buf = 0;//图像数组
int last = 0;
// 临时图像
IplImage *mhi = 0; // MHI: 运动历史图像
IplImage *orient = 0; // 方向
IplImage *mask = 0; // 有效的运动掩码
IplImage *segmask = 0; // 运动分割映射
CvMemStorage* storage = 0; // 临时存储区 // parameters:
using namespace std;
// 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; // 获取当前时间,以秒为单位
CvSize size = cvSize(img->width, img->height); // 获取当前帧尺寸
int i, idx1 = last, idx2;
IplImage* silh;
CvSeq* seq;
CvRect comp_rect;
double count;
double angle;
CvPoint center;
double magnitude;
CvScalar color;
// 开始时为图像分配内存 or 帧尺寸改变时重新分配内存
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++){
//图像数组内的每一个图像都是单通道0数组
cvReleaseImage(&buf[i]);
buf[i] = cvCreateImage(size, IPL_DEPTH_8U, 1);
cvZero(buf[i]);
}
//释放各个已用过的图像内容,以便重新写入
cvReleaseImage(&mhi);
cvReleaseImage(&orient);
cvReleaseImage(&segmask);
cvReleaseImage(&mask);
//将mhi设置为0矩阵
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); //RGB帧图像格式转换为gray
idx2 = (last + 1) % N; // index of (last - (N-1))th frame
last = idx2;//往后面移动一个单位,为前一帧图像的所在位置下标
silh = buf[idx2];
// 相邻两帧的差
cvAbsDiff(buf[idx1], buf[idx2], silh);
cvThreshold(silh, silh, diff_threshold, 1, CV_THRESH_BINARY); // 对差图像做二值化
//去掉影像以更新历史图像,mhi更新,mhi内为一定时间内的差值图像和,二值化黑白图像
cvUpdateMotionHistory(silh, mhi, timestamp, MHI_DURATION); // 更新运动历史图像
cvShowImage("mhi",mhi);
// convert MHI to blue 8u image
// cvCvtScale的第四个参数 shift = (MHI_DURATION - timestamp)*255./MHI_DURATION
//相当于cvConvertScale(src,dst,scale,shift)
//计算结果:dst=src*scale+shift
// 控制帧差的消失速率 ,数组与数组相乘函数,由于不同的时间特性给帧的显示提供渐变作用
cvCvtScale(mhi, mask, 255. / MHI_DURATION,
(MHI_DURATION - timestamp)*255. / MHI_DURATION);
cvShowImage("mask", mask);
cvZero(dst);
cvMerge(mask, 0, 0, 0, dst);//将几个单通道图像合并为一个多通道图像,只有蓝色
// B,G,R,0 convert to BLUE image
// 计算运动的梯度方向以及正确的方向掩码
// Filter size = 3
//参数:在未渐变的图像中去除大梯度
//历史图像,梯度正确的点,点的梯度方向,梯度值的范围规定
cvCalcMotionGradient(mhi, mask, orient,
MAX_TIME_DELTA, MIN_TIME_DELTA, 3);
cvShowImage("orient", orient);
if (!storage)
storage = cvCreateMemStorage(0);
else
cvClearMemStorage(storage);
// 运动分割: 获得运动部件的连续序列
//输入图像,输出图像,内存空间,当前时间,联系时间限制
seq = cvSegmentMotion(mhi, segmask, storage, timestamp, MAX_TIME_DELTA);
for (i = -1; i < seq->total; i++){
if (i < 0) { // 对整幅图像操作
//i=-1在序列中无具体操作,故在循环中加入为了得出全局运动的整体运动方向
//绘制结果应为白色大圆,为整体的运动方向
comp_rect = cvRect(0, 0, size.width, size.height);
color = CV_RGB(255, 255, 255);
magnitude = 100; // 画线长度以及圆半径的大小控制
}
else { // 第i个运动组件
//分割后的小运动方向标记
//为红色小圆
comp_rect = ((CvConnectedComp*)cvGetSeqElem(seq, i))->rect;
// 去掉小的部分
if (comp_rect.width + comp_rect.height < 100)
continue;
color = CV_RGB(255, 0, 0);
magnitude = 30;
//if(seq->total > 0) MessageBox(NULL,"Motion Detected",NULL,0);
}
//选择组件ROI
//感兴趣区域的设置,当i=-1时为整个图片的大小
//当i!=-1时,为每一个分割区域的大小
cvSetImageROI(silh, comp_rect);
cvSetImageROI(mhi, comp_rect);
cvSetImageROI(orient, comp_rect);
cvSetImageROI(mask, comp_rect);
// 在选择的区域内,计算运动方向 --计算全局运动的方向
angle = cvCalcGlobalOrientation(orient, mask, mhi, timestamp, MHI_DURATION);
angle = 360.0 - angle; //adjust for images with top-left origin
// 在轮廓内计算点数
// Norm(L1) = 所有像素值的和
count = cvNorm(silh, 0, CV_L1, 0);
//重置感兴趣区域
cvResetImageROI(mhi);
cvResetImageROI(orient);
cvResetImageROI(mask);
cvResetImageROI(silh);
// 检查小运动的情形
if (count < comp_rect.width*comp_rect.height * 0.05) // 像素的5%
continue;
// 画一个带箭头的记录以表示方向
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))),//cvRound四舍五入得到一个整型数字
color, 3, CV_AA, 0);
}
}
int main(){
IplImage* motion = 0;
CvCapture* capture = 0;
capture = cvCreateCameraCapture(0);
if (capture){
IplImage* image = cvQueryFrame(capture);
cvNamedWindow("Motion", 1);
for (;;){
image = cvQueryFrame(capture);
cvShowImage("Motion",image);
if (!motion)
{
motion = cvCreateImage(cvSize(image->width, image->height), 8, 3);
cvZero(motion);
//motion->origin = image->origin;
}
update_mhi(image, motion, 30);
cvShowImage("Motion", motion);
char c = cvWaitKey(10);
if (c == 27)
{
break;
}
}
cvReleaseCapture(&capture);
cvDestroyWindow("Motion");
}
return 0;
}
网友评论