http://wiki.opencv.org.cn/index.php/Cv运动分析与对象跟踪
http://wiki.opencv.org.cn/index.php/增强现实%28AR%29
opencv2中的segmentMotion内部调用的还是cvSegmentMotion这个函数。
void cv::segmentMotion(InputArray _mhi, OutputArray _segmask, vector<Rect>& boundingRects, double timestamp, double segThresh) { Mat mhi = _mhi.getMat(); _segmask.create(mhi.size(), CV_32F); CvMat c_mhi = mhi, c_segmask = _segmask.getMat(); Ptr<CvMemStorage> storage = cvCreateMemStorage(); Seq<CvConnectedComp> comps = cvSegmentMotion(&c_mhi, &c_segmask, storage, timestamp, segThresh); Seq<CvConnectedComp>::const_iterator it(comps); size_t i, ncomps = comps.size(); boundingRects.resize(ncomps); for( i = 0; i < ncomps; i++, ++it) boundingRects[i] = (*it).rect; }
CvSeq* cvSegmentMotion( const CvArr* mhi, CvArr* seg_mask, CvMemStorage* storage, double timestamp, double seg_thresh ); mhi 运动历史图像 seg_mask 发现应当存储的 mask 的图像, 单通道, 32bits, 浮点数. storage 包含运动连通域序列的内存存储仓 timestamp 当前时间,毫秒单位 seg_thresh 分割阈值,推荐等于或大于运动历史“每步”之间的间隔。 函数 cvSegmentMotion 寻找所有的运动分割,并且在seg_mask 用不同的单独数字(1,2,...)标识它们。
它也返回一个具有 CvConnectedComp 结构的序列,其中每个结构对应一个运动部件。
在这之后,每个运动部件的运动方向就可以被函数 cvCalcGlobalOrientation 利用提取的特定部件的掩模(mask)计算出来(使用 cvCmp)
(1)cvSegmentMotion示例代码
#include "stdafx.h" #include "opencv2/video/tracking.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc_c.h" #include <time.h> #include <stdio.h> #include <ctype.h> static void help(void) { printf( "\nThis program demonstrated the use of motion templates -- basically using the gradients\n" "of thresholded layers of decaying frame differencing. New movements are stamped on top with floating system\n" "time code and motions too old are thresholded away. This is the 'motion history file'. The program reads from the camera of your choice or from\n" "a file. Gradients of motion history are used to detect direction of motoin etc\n" "Usage :\n" "./motempl [camera number 0-n or file name, default is camera 0]\n" ); } // 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 static 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])); } 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 ); cvMerge( 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; help(); // if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0]))) // capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] - '0' : 0 ); // else if( argc == 2 ) // capture = cvCaptureFromFile( argv[1] ); capture = cvCaptureFromCAM( 0 ); if( capture ) { cvNamedWindow( "Motion", 1 ); for(;;) { IplImage* image = cvQueryFrame( capture ); if( !image ) break; 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 ); if( cvWaitKey(10) >= 0 ) break; } cvReleaseCapture( &capture ); cvDestroyWindow( "Motion" ); } return 0; } #ifdef _EiC main(1,"motempl.c"); #endif(2)测试效果