1. 程式人生 > >影象處理之其他雜項(一)之MeanShift的目標跟蹤演算法opencv c++程式碼 VS2015+opencv3.2

影象處理之其他雜項(一)之MeanShift的目標跟蹤演算法opencv c++程式碼 VS2015+opencv3.2

//#include "stdafx.h"  
//#include "cv.h"  
//#include "highgui.h"  
#include<opencv.hpp>
#define  u_char unsigned char  
#define  DIST 0.5  
#define  NUM 20  
using namespace std;
using namespace cv;
//全域性變數  
bool pause = false;
bool is_tracking = false;
Rect drawing_box;
Mat current;
double *hist1, *hist2;
double *m_wei;
double C = 0.0;
bool xx = false;
bool g_bDrawingBox = false;
Mat tempImage;
void ShowHelpText();
void init_target(double *hist1, double *m_wei, Mat current)
{
	int t_h, t_w, t_x, t_y;
	double h, dist;
	int i, j;
	int q_r, q_g, q_b, q_temp;

	t_h = drawing_box.height;
	t_w = drawing_box.width;
	t_x = drawing_box.x;
	t_y = drawing_box.y;

	h = pow(((double)t_w) / 2, 2) + pow(((double)t_h) / 2, 2);            //頻寬  
	Mat pic_hist = Mat(300, 200, CV_8UC3);     //生成直方圖影象  


	for (i = 0; i < t_w*t_h; i++)
	{
		m_wei[i] = 0.0;
	}

	for (i = 0; i < 4096; i++)
	{
		hist1[i] = 0.0;
	}

	for (i = 0; i < t_h; i++)
	{
		for (j = 0; j < t_w; j++)
		{
			dist = pow(i - (double)t_h / 2, 2) + pow(j - (double)t_w / 2, 2);
			m_wei[i * t_w + j] = 1 - dist / h;
			//printf("%f\n",m_wei[i * t_w + j]);  
			C += m_wei[i * t_w + j];
		}
	}

	//計算目標權值直方  
	for (i = t_y; i < t_y + t_h; i++)
	{
		for (j = t_x; j < t_x + t_w; j++)
		{
			//rgb顏色空間量化為16*16*16 bins  
			/*q_r = ((u_char)current.at(i + j * 3 + 2) / 16;
			q_g = ((u_char)current->imageData[i * current->widthStep + j * 3 + 1]) / 16;
			q_b = ((u_char)current->imageData[i * current->widthStep + j * 3 + 0]) / 16;*/
			q_r = current.at<Vec3b>(i, j)[2] / 16;
			q_g = current.at<Vec3b>(i, j)[1] / 16;
			q_b = current.at<Vec3b>(i, j)[0] / 16;
			q_temp = q_r * 256 + q_g * 16 + q_b;
			hist1[q_temp] = hist1[q_temp] + m_wei[(i - t_y) * t_w + (j - t_x)];
		}
	}

	//歸一化直方圖  
	for (i = 0; i < 4096; i++)
	{
		hist1[i] = hist1[i] / C;
		//printf("%f\n",hist1[i]);  
	}

	//生成目標直方圖  
	double temp_max = 0.0;

	for (i = 0; i < 4096; i++)         //求直方圖最大值,為了歸一化  
	{
		//printf("%f\n",val_hist[i]);  
		if (temp_max < hist1[i])
		{
			temp_max = hist1[i];
		}
	}
	//畫直方圖  
	Point p1, p2;
	double bin_width = pic_hist.cols / 4096;
	double bin_unith = pic_hist.rows / temp_max;

	for (i = 0; i < 4096; i++)
	{
		p1.x = i * bin_width;
		p1.y = pic_hist.rows;
		p2.x = (i + 1)*bin_width;
		p2.y = pic_hist.rows - hist1[i] * bin_unith;
		//printf("%d,%d,%d,%d\n",p1.x,p1.y,p2.x,p2.y);  
		rectangle(pic_hist, p1, p2, Scalar(0, 255, 0), -1, 8, 0);
	}
	imwrite("hist1.jpg", pic_hist);
	//cvReleaseImage(&pic_hist);
}

void MeanShift_Tracking(Mat current)
{
	int num = 0, i = 0, j = 0;
	int t_w = 0, t_h = 0, t_x = 0, t_y = 0;
	double *w = 0, *hist2 = 0;
	double sum_w = 0, x1 = 0, x2 = 0, y1 = 2.0, y2 = 2.0;
	int q_r, q_g, q_b;
	int *q_temp;
	//Mat pic_hist;

	t_w = drawing_box.width;
	t_h = drawing_box.height;

	Mat pic_hist = Mat(300, 200, CV_8UC3);     //生成直方圖影象  
	hist2 = (double *)malloc(sizeof(double) * 4096);
	w = (double *)malloc(sizeof(double) * 4096);
	q_temp = (int *)malloc(sizeof(int)*t_w*t_h);

	while ((pow(y2, 2) + pow(y1, 2) > 0.5) && (num < NUM))
	{
		num++;
		t_x = drawing_box.x;
		t_y = drawing_box.y;
		memset(q_temp, 0, sizeof(int)*t_w*t_h);
		for (i = 0; i < 4096; i++)
		{
			w[i] = 0.0;
			hist2[i] = 0.0;
		}

		for (i = t_y; i < t_h + t_y; i++)
		{
			for (j = t_x; j < t_w + t_x; j++)
			{
				//rgb顏色空間量化為16*16*16 bins  
				/*q_r = ((u_char)current->imageData[i * current->widthStep + j * 3 + 2]) / 16;
				q_g = ((u_char)current->imageData[i * current->widthStep + j * 3 + 1]) / 16;
				q_b = ((u_char)current->imageData[i * current->widthStep + j * 3 + 0]) / 16;*/
				q_r = current.at<Vec3b>(i, j)[2] / 16;
				q_g = current.at<Vec3b>(i, j)[1] / 16;
				q_b = current.at<Vec3b>(i, j)[0] / 16;
				q_temp[(i - t_y) *t_w + j - t_x] = q_r * 256 + q_g * 16 + q_b;
				hist2[q_temp[(i - t_y) *t_w + j - t_x]] = hist2[q_temp[(i - t_y) *t_w + j - t_x]] + m_wei[(i - t_y) * t_w + j - t_x];
			}
		}

		//歸一化直方圖  
		for (i = 0; i < 4096; i++)
		{
			hist2[i] = hist2[i] / C;
			//printf("%f\n",hist2[i]);  
		}
		//生成目標直方圖  
		double temp_max = 0.0;

		for (i = 0; i < 4096; i++)         //求直方圖最大值,為了歸一化  
		{
			if (temp_max < hist2[i])
			{
				temp_max = hist2[i];
			}
		}
		//畫直方圖  
		Point p1, p2;
		double bin_width = pic_hist.cols / (4368);
		double bin_unith = pic_hist.rows / temp_max;

		for (i = 0; i < 4096; i++)
		{
			p1.x = i * bin_width;
			p1.y = pic_hist.cols;
			p2.x = (i + 1)*bin_width;
			p2.y = pic_hist.rows - hist2[i] * bin_unith;
			rectangle(pic_hist, p1, p2, Scalar(0, 255, 0), -1, 8, 0);
		}
		imwrite("hist2.jpg", pic_hist);

		for (i = 0; i < 4096; i++)
		{
			if (hist2[i] != 0)
			{
				w[i] = sqrt(hist1[i] / hist2[i]);
			}
			else
			{
				w[i] = 0;
			}
		}

		sum_w = 0.0;
		x1 = 0.0;
		x2 = 0.0;

		for (i = 0; i < t_h; i++)
		{
			for (j = 0; j < t_w; j++)
			{
				//printf("%d\n",q_temp[i * t_w + j]);  
				sum_w = sum_w + w[q_temp[i * t_w + j]];
				x1 = x1 + w[q_temp[i * t_w + j]] * (i - t_h / 2);
				x2 = x2 + w[q_temp[i * t_w + j]] * (j - t_w / 2);
			}
		}
		y1 = x1 / sum_w;
		y2 = x2 / sum_w;

		//中心點位置更新  
		drawing_box.x += y2;
		drawing_box.y += y1;

	}

	rectangle(current, Point(drawing_box.x, drawing_box.y), Point(drawing_box.x + drawing_box.width, drawing_box.y + drawing_box.height), Scalar(255, 0, 0), 2);
	imshow("Meanshift", current);
}

void onMouse(int event, int x, int y, int flags, void *param)
{

	if (pause)
	{
		switch (event)
		{
		case EVENT_MOUSEMOVE:
			if (g_bDrawingBox)
			{

				drawing_box.width = x - drawing_box.x;
				drawing_box.height = y - drawing_box.y;

			}
			break;
		case CV_EVENT_LBUTTONDOWN:
			g_bDrawingBox = true;
			drawing_box = Rect(x, y, 0, 0);//記錄起始點//the left up point of the rect  
			//drawing_box.x = x;
			//drawing_box.y = y;

			break;
		case CV_EVENT_LBUTTONUP:
			//finish drawing the rect (use color green for finish)  
			g_bDrawingBox = false;
			xx = true;
			drawing_box.width = x - drawing_box.x;
			drawing_box.height = y - drawing_box.y;
			rectangle(current, drawing_box.tl(), drawing_box.br(), Scalar(255, 0, 0), 2);

			//目標初始化  
			hist1 = (double *)malloc(sizeof(double) * 16 * 16 * 16);
			m_wei = (double *)malloc(sizeof(double)*drawing_box.height*drawing_box.width);
			init_target(hist1, m_wei, current);
			is_tracking = true;
			//imshow("Meanshift", current);
			break;
		}
		imshow("Meanshift", current);
		return;
	}
}

int main()
{

	ShowHelpText();
	VideoCapture capture("E://dd4.mp4");
	capture.grab(); //從視訊檔案或捕獲裝置獲取下一幀  
	capture.retrieve(current);//解碼並返回抓取了的視訊幀  

	while (1)
	{

		if (is_tracking)
		{
			MeanShift_Tracking(current);
		}

		int c = waitKey(1);
		//暫停  
		if (c == 's')
		{
			while (1)
			{
				pause = true;
				setMouseCallback("Meanshift", onMouse, 0);
				current.copyTo(tempImage);
				if (g_bDrawingBox)
					rectangle(tempImage, drawing_box.tl(), drawing_box.br(), Scalar(255, 0, 0), 2);
				if (xx == true)
					break;
				waitKey(2);
				imshow("Meanshift", tempImage);
			}
		}

		while (pause)
		{
			if (waitKey(0) == 's')
				pause = false;
		}
		//if (current.size != 0)
		try { imshow("Meanshift", current); }
		catch (exception& e) { cout << "視訊播放完畢"; return 0; }
		//else
			//break;
		capture.grab(); //從視訊檔案或捕獲裝置獲取下一幀  
		capture.retrieve(current);//解碼並返回抓取了的視訊幀  								  
	}

	namedWindow("Meanshift", 1);
	return 0;
}
void ShowHelpText()
{
	//輸出歡迎資訊和OpenCV版本
	printf("\n1.這是根據原有作者的c介面opencv升級的c++介面程式,適用於opencv2.0版本,開發環境為VS2015+opencv3.2\n");
	printf("\n2.程式視訊的預設路徑為(E\\dd4.mp4)\n");
	printf("\n3.視訊啟動後按 s 鍵進行暫停,然後用滑鼠選取需要跟蹤的矩形框,再次按 s 視訊啟動進行跟蹤\n");
	printf("\n4.程式有個bug,視訊播放完畢會報錯");
	//printf("\n\n  ----------------------------------------------------------------------------\n");
	//輸出一些幫助資訊
	//printf("\n\n\n\t歡迎來到【滑鼠互動演示】示例程式\n");
	//printf("\n\n\t請在視窗中點選滑鼠左鍵並拖動以繪製矩形\n");

}