Camshift跟踪(上)

这里有几个函数:

mixChannels()

  1. Mat rgba( 3, 4, CV_8UC4, Scalar(1,2,3,4) );  
  2. Mat bgr( rgba.rows, rgba.cols, CV_8UC3 );  
  3. Mat alpha( rgba.rows, rgba.cols, CV_8UC1 );  
  4.   
  5. // forming an array of matrices is a quite efficient operation,  
  6. // because the matrix data is not copied, only the headers  
  7. Mat out[] = { bgr, alpha };  
  8. // rgba[0] -> bgr[2], rgba[1] -> bgr[1],  
  9. // rgba[2] -> bgr[0], rgba[3] -> alpha[0]  
  10. int from_to[] = { 0,2, 1,1, 2,0, 3,3 };  
  11. mixChannels( &rgba, 1, out, 2, from_to, 4 );  
运行结果:


Camshift跟踪(上)_第1张图片


inRange()

inRange用来检查元素的取值范围是否在另两个矩阵的元素取值之间,返回验证矩阵mask(0-1矩阵)


calcHist()

calcHist()函数第一个参数为输入矩阵序列,第2个参数表示输入的矩阵数目,第3个参数表示将被计算直方图维数通道的列表,第4个参数表示可选的掩码函数
第5个参数表示输出直方图,第6个参数表示直方图的维数,第7个参数为每一维直方图数组的大小,第8个参数为每一维直方图bin的边界


以下为demo代码:

// camshift.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <OpenCV245.h>


using namespace std;
using namespace cv;


Mat image;

bool backprojMode = false;  //表示是否要进入反向投影模式,ture表示准备进入反向投影模式
bool selectObject = false;  //用来判断是否选中,当鼠标左键按下时为ture,左键松开时为false

int trackObject = 0;        //代表跟踪目标数目

bool showHist = true;       //是否显示直方图

Point origin;               //选中的起点
Rect selection;             //选中的区域

int vmin = 10, vmax = 256, smin = 30;


//鼠标事件响应函数,这个函数从按下左键时开始响应直到左键释放
static void onMouse(int event, int x, int y, int, void* )
{
	if( selectObject )
	{
		selection.x = MIN(x, origin.x);
		selection.y = MIN(y, origin.y);

		selection.width = abs(x - origin.x);
		selection.height = abs(y - origin.y);
	}


	switch (event)
	{
	case CV_EVENT_LBUTTONDOWN:  //按下鼠标时,捕获点origin

		origin = Point(x, y);
		selection = Rect(x, y, 0, 0);
		selectObject = true;
		break;

	case CV_EVENT_LBUTTONUP:

		selectObject = false;

		if ( selection.width > 0 && selection.height > 0)
		{
			trackObject = -1;
		}
		break;
	}
}


const char* keys = 
{
		    "{1|  | 0 | camera number}"
};


//const char* keys = { "{1| | 0 | camera number}" };

int _tmain(int argc, _TCHAR* argv[])
{

	VideoCapture cap;
	Rect trackWindow; //跟踪的窗口
	int hsize = 16; //创建直方图时要用的常量
	float hranges[] = {0, 180};
	const float* phranges = hranges;
	CommandLineParser parser(argc, argv, keys);//命令解析器函数

	//CommandLineParser parser(argc, argv, keys);
	int camNum = parser.get<int>("0");

	cap.open(camNum);

	if ( !cap.isOpened())
	{
		cout << "***Could not initialize capturing...***\n";  
		cout << "Current parameter's value: \n";  
		parser.printParams(); 
		return -1;  
	}

	//关于显示窗口的一些设置
	namedWindow("Histogram", 0);
	namedWindow("CamShift Demo", 0);

	//设置鼠标事件,把鼠标响应与onMouse函数关联起来
	setMouseCallback("CamShift Demo", onMouse, 0);

	//创建三个滑块条,特定条件用滑块条选择不同参数能获得较好的跟踪效果  
	createTrackbar( "Vmin", "CamShift Demo", &vmin, 256, 0 );  
	createTrackbar( "Vmax", "CamShift Demo", &vmax, 256, 0 );  
	createTrackbar( "Smin", "CamShift Demo", &smin, 256, 0 );  

	Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj;
	bool paused = false;


	for(;;)
	{
		if ( !paused ) //没有暂停
		{
			cap >> frame; //从摄像头输入frame
			if(frame.empty())
				break;
		}

		frame.copyTo(image);

		if( !paused )
		{
			cvtColor(image, hsv, CV_BGR2HSV);

			if( trackObject )   //松开鼠标左键时,trackObject为-1,执行核心部分
			{
				int _vmin = vmin, _vmax = vmax;  //int vmin = 10, vmax = 256, smin = 30;

				//inRange用来检查元素的取值范围是否在另两个矩阵的元素取值之间,返回验证矩阵mask(0-1矩阵)  
				//这里用于制作掩膜板,只处理像素值为H:0~180,S:smin~256, V:vmin~vmax之间的部分。mask是要求的,单通道  

				inRange(hsv, Scalar(0, smin, MIN(_vmin, _vmax)),
					Scalar(180, 256, MAX(_vmin, _vmax)), mask);

				int ch[] = {0, 0};

				hue.create(hsv.size(), hsv.depth());
				mixChannels(&hsv, 1, &hue, 1, ch, 1);//将H分量拷贝到hue中,其他分量不拷贝

				if ( trackObject < 0 )
				{
					Mat roi(hue, selection), maskroi(mask, selection);

					//calcHist()函数第一个参数为输入矩阵序列,第2个参数表示输入的矩阵数目,第3个参数表示将被计算直方图维数通道的列表,第4个参数表示可选的掩码函数
					//第5个参数表示输出直方图,第6个参数表示直方图的维数,第7个参数为每一维直方图数组的大小,第8个参数为每一维直方图bin的边界
					calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
					normalize(hist, hist, 0, 255, CV_MINMAX);


					trackWindow = selection;
					trackObject = 1;//只要鼠标选完区域松开后,且没有按键盘清0键'c',则trackObject一直保持为1,因此该if函数只能执行一次,除非重新选择跟踪区域

					histimg = Scalar::all(0); //与按下'c'键是一样的,这里的all(0)表示的是标量全部清0

					int binw = histimg.cols / hsize;//histing是一个200*300的矩阵,hsize应该是每一个bin的宽度,也就是histing矩阵能分出几个bin出来
					Mat buf(1, hsize, CV_8UC3);

					for( int i = 0; i < hsize; i++ )
						buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180/hsize), 255, 255);

					cvtColor(buf, buf, CV_HSV2BGR);

					for( int i = 0; i < hsize; i++ )
					{
						int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255);

						rectangle(histimg, Point(i*binw, histimg.rows), 
							Point((i + 1)*binw, histimg.rows - val), Scalar(buf.at<Vec3b>(i)), -1, 8);
					}

				}

				calcBackProject(&hue, 1, 0, hist, backproj, &phranges);

				//计算两个矩阵backproj、mask的每个元素的按位与,返回backproj
				backproj &= mask;

				RotatedRect trackBox = CamShift(backproj, trackWindow, TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));

				if( trackWindow.area() <= 1 )
				{
					int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;

					trackWindow = Rect(trackWindow.x - r, trackWindow.y - r, trackWindow.x + r, trackWindow.y + r) &
						Rect(0, 0, cols, rows);
				}

				if (backprojMode)
					cvtColor( backproj, image, CV_GRAY2BGR);

				ellipse(image, trackBox, Scalar(0, 0, 255), 3, CV_AA);

			}
		}

		else if( trackObject < 0 )
			paused = false;

		if( selectObject && selection.width > 0 && selection.height > 0 )  
		{  
			Mat roi(image, selection);  
			bitwise_not(roi, roi);  
		}  

		imshow( "CamShift Demo", image );  
		imshow( "Histogram", histimg );  

		//每轮都要等待用户的按键控制  
		char c = (char)waitKey(10);  
		if( c == 27 )//"Esc"键,直接退出  
			break;  
		switch(c)  
		{  
		case 'b'://转换显示方式  
			backprojMode = !backprojMode;  
			break;  
		case 'c'://停止追踪  
			trackObject = 0;  
			histimg = Scalar::all(0);  
			break;  
		case 'h'://隐藏或显示直方图  
			showHist = !showHist;  
			if( !showHist )  
				destroyWindow( "Histogram" );  
			else  
				namedWindow( "Histogram", 1 );  
			break;  
		case 'p'://暂停  
			paused = !paused;//frame停止从摄像头获取图像,只显示旧的图像  
			break;  
		default:  
			;  
		}  
	}

	return 0;
}

参考链接:http://blog.csdn.net/jkhere/article/details/8696347

你可能感兴趣的:(Camshift跟踪(上))