⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 optical_flow_klt.cpp

📁 KLT方法对特征点进行跟踪
💻 CPP
字号:
//////////////////////////////////////////////////
//实现特征点的跟踪
//利用函数cvGoodFeaturesToTrack
///////////////////////////////////////////////////
#include <stdio.h>
#include <cv.h>
#include <highgui.h>
#include <math.h>

/******************************/
//注意:这里N为要跟踪的特征点个数,通过修改N的值即可
static const int N= 100;
/******************************/

static const double pi = 3.14159265358979323846;

inline static double square(int a)
{
	return a * a;
}

//定义内联函数,减少栈内存的消耗 定义的图像的大小,深度和信道(channels)
inline static void allocateOnDemand( IplImage **img, CvSize size, int depth, int channels )
{
	if ( *img != NULL )	return;

	*img = cvCreateImage( size, depth, channels );
	if ( *img == NULL )
	{
		fprintf(stderr, "Error: Couldn't allocate image.  Out of memory?\n");
		exit(-1);
	}
}

int main(void)
{

//	CvCapture *input_video = cvCaptureFromCAM(0);
    CvCapture *input_video = cvCaptureFromFile("urban.avi");
//	CvCapture *input_video = cvCaptureFromFile("SampleVideo.avi");

	if (input_video == NULL)
	{
		fprintf(stderr, "Error: Can't open video.\n");
		return -1;
	}

	/* 读取视频(AVI)帧图像的大小 */
	CvSize frame_size;
	frame_size.height =
		(int) cvGetCaptureProperty( input_video, CV_CAP_PROP_FRAME_HEIGHT );
	frame_size.width =
		(int) cvGetCaptureProperty( input_video, CV_CAP_PROP_FRAME_WIDTH );

	/* 设定视频AVI的帧数 */
	long number_of_frames;

	/* Go to the end of the AVI (ie: the fraction is "1") */
	cvSetCaptureProperty( input_video, CV_CAP_PROP_POS_AVI_RATIO, 1. );
	//CV_CAP_PROP_POS_AVI_RATIO表示视频文件的相对位置(0 - 影片的开始,1 - 影片的结尾)

	/* Now that we're at the end, read the AVI position in frames */
	number_of_frames = (int) cvGetCaptureProperty( input_video, CV_CAP_PROP_POS_FRAMES );
	//CV_CAP_PROP_POS_FRAMES - 将被下一步解压/获取的帧索引,以0为起点
	
	/* Return to the beginning */
	/*CV_CAP_PROP_POS_FRAMES - 单位为帧数的位置(只对视频文件有效)*/
	cvSetCaptureProperty( input_video, CV_CAP_PROP_POS_FRAMES, 0. );

	/* Create a windows called "Optical Flow KLT" for visualizing the output.*/
	cvNamedWindow("Optical Flow KLT", CV_WINDOW_AUTOSIZE);

	long current_frame = 0;
	while(true)
	{
		static IplImage *frame = NULL, *frame1 = NULL, *frame1_1C = NULL, *frame2_1C = NULL,
			*eig_image = NULL, *temp_image = NULL, *pyramid1 = NULL, *pyramid2 = NULL;

	
		cvSetCaptureProperty( input_video, CV_CAP_PROP_POS_FRAMES, current_frame );

		/* Get the next frame of the video*/
		frame = cvQueryFrame( input_video );
		if (frame == NULL)
		{
			fprintf(stderr, "Error: Hmm. The end came sooner than we thought.\n");
			return -1;
		}
		/* Allocate another image if not already allocated.
		 * Image has ONE channel of color (ie: monochrome) with 8-bit "color" depth.
		 */
		allocateOnDemand( &frame1_1C, frame_size, IPL_DEPTH_8U, 1 );
		/* Convert whatever the AVI image format is into OpenCV's preferred format.
		 * AND flip the image vertically.  Flip is a shameless hack.  OpenCV reads
		 * in AVIs upside-down by default. 
		 */
		//CV_CVTIMG_FLIP - 垂直翻转图像
		cvConvertImage(frame, frame1_1C, CV_CVTIMG_FLIP);

		/* We'll make a full color backup of this frame so that we can draw on it.
		 * (It's not the best idea to draw on the static memory space of cvQueryFrame().)
		 */
		allocateOnDemand( &frame1, frame_size, IPL_DEPTH_8U, 3 );
		cvConvertImage(frame, frame1, CV_CVTIMG_FLIP);

		/* Get the second frame of video.  Same principles as the first. */
		frame = cvQueryFrame( input_video );
		if (frame == NULL)
		{
			fprintf(stderr, "Error: Hmm. The end came sooner than we thought.\n");
			return -1;
		}
		allocateOnDemand( &frame2_1C, frame_size, IPL_DEPTH_8U, 1 );
		cvConvertImage(frame, frame2_1C, CV_CVTIMG_FLIP);

		/* Shi and Tomasi Feature Tracking! */

		/* Preparation: Allocate the necessary storage. */
		allocateOnDemand( &eig_image, frame_size, IPL_DEPTH_32F, 1 );
		allocateOnDemand( &temp_image, frame_size, IPL_DEPTH_32F, 1 );

		/* Preparation: This array will contain the features found in frame 1. */
		//设置第一帧的特征点数目
		CvPoint2D32f frame1_features[N];

		int number_of_features;		
		number_of_features = N;

		/* Actually run the Shi and Tomasi algorithm!!
		 * "frame1_1C" is the input image.
		 * "eig_image" and "temp_image" are just workspace for the algorithm.
		 * The first ".01" specifies the minimum quality of the features (based on the eigenvalues).
		 * The second ".01" specifies the minimum Euclidean distance between features.
		 * "NULL" means use the entire input image.  You could point to a part of the image.
		 * WHEN THE ALGORITHM RETURNS:
		 * "frame1_features" will contain the feature points.
		 * "number_of_features" will be set to a value <= 100 indicating the number of feature points found.
		 */
		/* quality_level =.02
			最大最小特征值的乘法因子。定义可接受图像角点的最小质量因子。 
			min_distance =10.0
			限制因子。得到的角点的最小距离。使用 Euclidian 距离 
		*/
		//number_of_features参数值为小于frame1_features[100]
		//函数 cvGoodFeaturesToTrack 在图像中寻找具有大特征值的角点
		
		cvGoodFeaturesToTrack(frame1_1C, eig_image, temp_image, frame1_features, 
			&number_of_features, .02, 10.0, NULL);

		/* Pyramidal Lucas Kanade Optical Flow! */

		/* This array will contain the locations of the points from frame 1 in frame 2. */
		//设置第一帧的特征点数目
		CvPoint2D32f frame2_features[N];

		char optical_flow_found_feature[N];

		/* The i-th element of this array is the error in the optical flow for the i-th feature
		 * of frame1 as found in frame 2. */
		float optical_flow_feature_error[N];

		/* This is the window size to use to avoid the aperture problem  */
		CvSize optical_flow_window = cvSize(3,3);
		
		/* This termination criteria tells the algorithm to stop when it has either done 20 iterations or when
		 * epsilon is better than .3.  You can play with these parameters for speed vs. accuracy but these values
		 * work pretty well in many situations.
		 */
		CvTermCriteria optical_flow_termination_criteria
			= cvTermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, .3 );

		/* This is some workspace for the algorithm.
		 * (The algorithm actually carves the image into pyramids of different resolutions.)
		 */
		allocateOnDemand( &pyramid1, frame_size, IPL_DEPTH_8U, 1 );
		allocateOnDemand( &pyramid2, frame_size, IPL_DEPTH_8U, 1 );

		/* Actually run Pyramidal Lucas Kanade Optical Flow!!
		 * "frame1_1C" is the first frame with the known features.
		 * "frame2_1C" is the second frame where we want to find the first frame's features.
		 * "pyramid1" and "pyramid2" are workspace for the algorithm.
		 * "frame1_features" are the features from the first frame.
		 * "frame2_features" is the (outputted) locations of those features in the second frame.//下一帧特征点
		 * "number_of_features" is the number of features in the frame1_features array.
		 * "optical_flow_window" is the size of the window to use to avoid the aperture problem.//孔径问题
		 * "5" is the maximum number of pyramids to use.  0 would be just one level.//图像金字塔
		 * "optical_flow_found_feature" is as described above (non-zero if feature found by the flow).
		 * "optical_flow_feature_error" is as described above (error in the flow for this feature).
		 * "optical_flow_termination_criteria" is as described above (how long the algorithm should look).
		 * "0" means disable enhancements.  (For example, the second array isn't pre-initialized with guesses.)
		 */
		//光流参数设置(重点看!!!)
		cvCalcOpticalFlowPyrLK(frame1_1C, frame2_1C, pyramid1, pyramid2, frame1_features, frame2_features,
			number_of_features, optical_flow_window, 5, optical_flow_found_feature, 
			optical_flow_feature_error, optical_flow_termination_criteria, 0 );
		
		/* For fun , let's draw the flow field. */
		for(int i = 0; i < number_of_features; i++)
		{
			/* If Pyramidal Lucas Kanade didn't really find the feature, skip it. */
			//optical_flow_found_feature数组
			//如果对应特征的光流被发现,数组中的每一个元素都被设置为1,否则设置为0 
			if ( optical_flow_found_feature[i] == 0 )	continue;

			int line_thickness;				line_thickness = 1;
			/* CV_RGB(red, green, blue) is the red, green, and blue components
			 * of the color you want, each out of 255.*/	
			CvScalar line_color;			
			line_color = CV_RGB(255,0,0);
	
			/* Let's make the flow field look nice with arrows. */

			/* The arrows will be a bit too short for a nice visualization because of the high framerate
			 * (ie: there's not much motion between the frames).  So let's lengthen them by a factor of 3.
			 */
			CvPoint p,q;
			p.x = (int) frame1_features[i].x;
			p.y = (int) frame1_features[i].y;
			q.x = (int) frame2_features[i].x;
			q.y = (int) frame2_features[i].y;

			double angle;		angle = atan2( (double) p.y - q.y, (double) p.x - q.x );
			double hypotenuse;	hypotenuse = sqrt( square(p.y - q.y) + square(p.x - q.x) );

			/* Here we lengthen the arrow by a factor of three. */
			q.x = (int) (p.x - 3 * hypotenuse * cos(angle));
			q.y = (int) (p.y - 3 * hypotenuse * sin(angle));

			/* Now we draw the main line of the arrow. */
			/* "frame1" is the frame to draw on.
			 * "p" is the point where the line begins.
			 * "q" is the point where the line stops.
			 * "CV_AA" means antialiased drawing.
			 * "0" means no fractional bits in the center cooridinate or radius.
			 */
			cvLine( frame1, p, q, line_color, line_thickness, CV_AA, 0 );
			/* Now draw the tips of the arrow.  I do some scaling so that the
			 * tips look proportional to the main line of the arrow.
			 */	
			//设置箭头(没有什么实质性意义)
			p.x = (int) (q.x + 6 * cos(angle + pi / 4));
			p.y = (int) (q.y + 6 * sin(angle + pi / 4));
			cvLine( frame1, p, q, line_color, line_thickness, CV_AA, 0 );
			p.x = (int) (q.x + 6 * cos(angle - pi / 4));
			p.y = (int) (q.y + 6 * sin(angle - pi / 4));
			cvLine( frame1, p, q, line_color, line_thickness, CV_AA, 0 );
		}

		cvShowImage("Optical Flow KLT", frame1);
       
	    // 按键的设置,为了观察
		int key_pressed;
		key_pressed = cvWaitKey(0);

		/* 按b或B帧后退,按a或A帧前进,按s或S程序结束*/
		if (key_pressed == 'b' || key_pressed == 'B')	current_frame--;
		else if(key_pressed == 'a' || key_pressed == 'A')	{current_frame++; cvSaveImage("feature.jpg", frame1);}
		        else if(key_pressed == 's' || key_pressed == 'S')      break;
				
		/*限定帧的运行范围.*/
		if (current_frame < 0)						current_frame = 0;
		if (current_frame >= number_of_frames - 1)	current_frame = number_of_frames - 2;
	}
	return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -