opencv video鼠标选中的物体跟踪,算法calcOpticalFlowPyrLK

简介

  本篇讲解opencv video鼠标选中的物体跟踪,使用的是opencv提供的calcOpticalFlowPyrLK。

calcOpticalFlowPyrLK介绍

  void calcOpticalFlowPyrLK(InputArray prevImg, InputArray nextImg, InputArray prevPts, InputOutputArray nextPts,
                              OutputArray status, OutputArray err, Size winSize=Size(21,21), int maxLevel=3, 
                              TermCriteria criteria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01), 
                              int flags=0, double minEigThreshold=1e-4 );
    prevImg:前一帧video图像。
    nextImg:当前video图像。
    prevPts:前一帧video图像中被跟踪的坐标点。
    nextPts:prevPts保存的坐标点,在当前帧video图像中计算出来的对应坐标,也就是跟踪到的坐标点。
    winSize:在每层的搜索窗口的大小。
    criteria:算法递归停止的条件。
    。。。。。

具体实现

实现代码

#include "stdafx.h"


#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

#include 
#include 
#include 
//#include 
#include 

using namespace cv;
using namespace std;

vector point1, point2;
bool left_mouse = false;
Point2f point;
int pic_info[4];
Mat gray, prevGray, image;
const Scalar GREEN = Scalar(0, 255, 0);
int rect_width = 0, rect_height = 0;
Point tmpPoint;

static void onMouse(int event, int x, int y, int /*flags*/, void* /*param*/) {
	Mat mouse_show;
	image.copyTo(mouse_show);

	if (event == EVENT_LBUTTONDOWN)   //检测到鼠标事件是按下时,记录鼠标所在坐标值,矩形起始坐标值
	{
		pic_info[0] = x;
		pic_info[1] = y;
		left_mouse = true;
	}
	else if ((event == EVENT_MOUSEMOVE) && (left_mouse == true))  //left_mouse == true按下到抬起,检测到鼠标事件是抬起时,记录鼠标所在的矩形末坐标值
	{
		rectangle(mouse_show, Point(pic_info[0], pic_info[1]), Point(x, y), GREEN, 2);  //pic_info[0], pic_info[1]保存的是矩形的起始坐标;x, y保存的是矩形的末尾坐标
		imshow("LK Demo", mouse_show);
	}
	else if (event == EVENT_LBUTTONUP)   //鼠标抬起后,移走了
	{
#if 1
		rectangle(mouse_show, Point(pic_info[0], pic_info[1]), Point(x, y), GREEN, 2); //对起始坐标和末尾坐标画出矩形,带有矩形的图片保存到mouse_show
		
		x = (pic_info[0] + x) / 2;   //通过矩形的起始坐标和末尾坐标的x值,算出矩形中心x坐标
		y = (pic_info[1] + y) / 2;	 //通过矩形的起始坐标和末尾坐标的y值,算出矩形中心y坐标

		point = Point2f((float)x, (float)y); //将标记矩形的中心坐标x,y保存到point
		point1.clear();
		point2.clear();
		point1.push_back(point);      //中心坐标保存到栈point1中,为下面(!point1.empty())的判断,提供条件
		imshow("LK Demo", mouse_show);
#endif
		left_mouse = false;
	}

}

int main(int argc, char** argv)
{
	VideoCapture cap;
	TermCriteria termcrit(TermCriteria::COUNT | TermCriteria::EPS, 20, 0.03); //迭代算法的终止条件
	//TermCriteria termcrit(TermCriteria::COUNT | TermCriteria::EPS, 40, 0.001); //迭代算法的终止条件
	Size winSize(40, 40);

	//cap.open(argv[1]);
	cap = VideoCapture(0);//打开摄像头,从摄像头中获取视频
	if (!cap.isOpened()) {  //如果打开失败,返回
		cout << "Could not initialize capturing...\n";
		return 0;
	}

	namedWindow("LK Demo", 1);  //窗口命名LK Demo
	setMouseCallback("LK Demo", onMouse, 0);  //创建鼠标响应事件函数

	for (;;) {
		Mat frame;  //创建帧
		cap >> frame; //从摄像头中获取当前帧
		if (frame.empty())
			break;
		frame.copyTo(image);  //将当前帧从frame拷贝到image,现在image保存的是当前帧
		cvtColor(image, gray, COLOR_BGR2GRAY); //将mage保存的是当前帧由RGB转化成灰度GRAY图片,转化后的灰度图片保存到gray中
		if ((!point1.empty())) {  //如果point1栈中有x,y坐标值,这里的坐标值是在onMouse函数中添加进来的
			vector status;
			vector err;
			if (prevGray.empty())      //第一次prevGray中的图片是空的
				gray.copyTo(prevGray); //将当前的图片gray拷贝到prevGray
									
			/*使用金字塔Lucas&Kanade方法计算一个稀疏特征集的光流
			prevGray是前一帧图片,gray是后一帧图片,point1是前一
			帧图片中标记的方框中心坐标x,y的值,point2是计算出下
			一帧跟踪目标的中心坐标,winSize是计算的区域
			*/
			calcOpticalFlowPyrLK(prevGray, gray, point1, point2, status, err, winSize,3, termcrit, 0, 0.001); 
			tmpPoint = point2[0];

			/*
			在计算出新的一帧图片的中心坐标point2的x和y,括出大小40*40分辨率大小的方框,方框显示为 GREEN = Scalar(0, 255, 0);即红色,将处理后的图片保存到image
			*/
			rectangle(image, Point(tmpPoint.x - 20, tmpPoint.y - 20), Point(tmpPoint.x + 20, tmpPoint.y + 20), GREEN, 2);
		}

		/*
		将标有红色框框的图片显示出来
		*/
		imshow("LK Demo", image);
		waitKey(100);
		std::swap(point1,point2);  //将新标记的目标坐标复制到point1,因为再计算下一帧时,当前帧就变成了上一帧,要用到point1即上一帧的标记坐标
		cv::swap(prevGray, gray);  //将新一帧复制到prevGray,因为再计算下一帧时,要用到prevGray即上一帧
	}
	return 0;
}

代码讲解

  1、首先设置了算法calcOpticalFlowPyrLK将会使用到的递归停止条件(termcrit),关于termcrit的具体讲解,可以看这里有具体讲解:
http://blog.csdn.net/yang_xian521/article/details/6905244 ,接着打开视频文件,句柄保存在cap中。然后设置了显示窗口,已经它的鼠标响应函数。
 VideoCapture cap;
    TermCriteria termcrit(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 20, 0.03); //迭代算法的终止条件
    Size winSize(31,31);
 
    cap.open(argv[1]);
    if(!cap.isOpened()){
        cout << "Could not initialize capturing...\n";
        return 0;
    }
 
    namedWindow( "LK Demo", 1 );
    setMouseCallback( "LK Demo", onMouse, 0 );
  2、鼠标响应函数,主要做的就是,在当前video帧中画一个矩形,然后计算出该矩形的中心位置坐标,保存到point1中。这个位置坐标就是在
calcOpticalFlowPyrLK算法中用来跟踪的点。
static void onMouse( int event, int x, int y, int /*flags*/, void* /*param*/ ){
	Mat mouse_show;
	image.copyTo(mouse_show);
 
	if(event == CV_EVENT_LBUTTONDOWN){
		pic_info[0] = x;
		pic_info[1] = y;
		left_mouse = true;
	}else if(event == CV_EVENT_LBUTTONUP){
		rectangle(mouse_show, Point(pic_info[0], pic_info[1]), Point(x, y), GREEN, 2);
		rect_width = 009695399/functions/abs.html">abs(x - pic_info[0]);
		rect_height = 009695399/functions/abs.html">abs(y - pic_info[1]);
		x = (pic_info[0] + x) / 2;
		y = (pic_info[1] + y) / 2;
		point = Point2f((float)x, (float)y);
		point1.clear();
		point2.clear();
        point1.push_back(point);
        imshow("LK Demo", mouse_show);
		left_mouse = false;
	}else if((event == CV_EVENT_MOUSEMOVE) && (left_mouse == true)){
		rectangle(mouse_show, Point(pic_info[0], pic_info[1]), Point(x, y), GREEN, 2);
        imshow("LK Demo", mouse_show);
	}
}
  3、当用户还没有鼠标框选跟踪目标时候,软件会不断的读取出video的数据,保存到frame中,接着copy一份当前帧数据到gray中,并将gray中的
图像灰阶化,然后显示出video frame数据。最后交换了point2和point1中的坐标信息和保存了当前灰阶化后的帧率到prevGray中。
    for(;;){
        Mat frame;
        cap >> frame;
        if( frame.empty() )
            break;
        frame.copyTo(image);
        cvtColor(image, gray, COLOR_BGR2GRAY);
        ...........
        imshow("LK Demo", image);
		waitKey(100);
        std::swap(point2, point1);
        cv::swap(prevGray, gray);
    }
  4、最后当用户框选了跟踪目标之后,也就是point1不为空之后,开始用calcOpticalFlowPyrLK跟踪计算,注意传入该函数的参数:prevGray相当于
之前保存的前一帧的数据;gray是当前帧数据;point1是前一帧中被跟踪的目标位置;point2是计算出来的被跟踪目标在当前帧的位置。
  最后用计算出来的在当前帧中,跟踪目标坐标point2作为中心,在当前帧中画出一个40X40的矩形作为标记,最后显示出来。
        if((!point1.empty())){
            vector status;
            vector err;
            if(prevGray.empty())
                gray.copyTo(prevGray);
            calcOpticalFlowPyrLK(prevGray, gray, point1, point2, status, err, winSize,
                                 3, termcrit, 0, 0.001); //使用金字塔Lucas&Kanade方法计算一个稀疏特征集的光流
			tmpPoint = point2[0];
			rectangle(image, Point(tmpPoint.x - 20, tmpPoint.y - 20), Point(tmpPoint.x + 20, tmpPoint.y + 20), GREEN, 2);
        }

效果演示

  对应的效果演示如下:                                        

opencv video鼠标选中的物体跟踪,算法calcOpticalFlowPyrLK_第1张图片

你可能感兴趣的:(机器视觉开发专栏,opencv)