均值漂移(meanshift)算法查找物体

均值漂移算法以迭代的方式锁定概率函数的局部最大值,它的原理是寻找预定义窗口中数据点的重心,或者说加权平均值。将窗口重心移动到数据点的重心处,并重复这个过程直到窗口重心收敛到一个稳定点。

反投影直方图的结果是一个概率映射,作用在于替换一个输入图像中的每个像素值,使其变成归一化直方图中对应的概率值,体现了已知图像的特定内容出现在图像中特定位置的概率。

下面一个简单的例子演示如何利用meanshift算法查找物体。

主要步骤:选定ROI,计算并归一化ROI的直方图,根据得到的直方图计算出ROI在色调通道的反投影直方图,代入meanshift算法更新ROI的位置。整个过程使用HSV颜色空间的Hue通道来描述物体。

#include"cv.h"
#include"highgui.h"
#include <iostream>
using namespace std;
using namespace cv;

Mat image;//最终显示图像
Mat histimg = Mat(200, 300, CV_8UC3,Scalar::all(0));//最终显示直方图

const char* keys = {
	"{camera|camera num|0|The PC'camera number}"
};
string err_cp = "\n\t\topen camera failed\n\t\t";
Point origin;//用于保存鼠标选择第一次单击时点的位置
Rect selection;//用于保存鼠标选择的矩形框
Rect trackWindow;//追踪的选择区域
int trackObject = 0; //代表跟踪目标数目
bool selectObject = false;//代表是否在选要跟踪的初始目标,true表示正在用鼠标选择
bool showHist = true;
bool pause = false;
int * channels = { 0 };
float range[2] = { 0, 180 };
const float * hranges = range;

void mousecallback(int event,int x,int y,int flags,void* param){
	if (selectObject)//只有当鼠标左键按下去时才有效,然后通过if里面代码就可以确定所选择的矩形区域selection了
	{
		selection.x = MIN(x, origin.x);//矩形左上角顶点坐标
		selection.y = MIN(y, origin.y);
		selection.width = std::abs(x - origin.x);//矩形宽
		selection.height = std::abs(y - origin.y);//矩形高
		selection &= Rect(0, 0, image.cols, image.rows);//用于确保所选的矩形区域在图片范围内
	}
	switch (event){
	case CV_EVENT_LBUTTONDOWN:
		origin = Point(x, y);
		selection = Rect(x, y, 0, 0);//鼠标刚按下去时初始化了一个矩形区域
		selectObject = true;
		break;
	case CV_EVENT_LBUTTONUP:
		selectObject = false;
		trackObject = 0; //代表跟踪目标数目
		if (selection.width > 0 && selection.height > 0)
			trackObject = -1;
		break;
	}
}
void direct(){
	cout << "\n\n\t图像跟踪程序\n"
		<< "\t\t鼠标左键选择ROI进行跟踪\n"
		<< "\t\tq--退出程序\n"
		<< "\t\th--显示/关闭直方图\n"
		<< "\t\tp--程序暂停\n"
		<< "\t\tc--清除ROI\n";
}
int main(int argc, char** argv){
	system("color 5E");
	direct();
	VideoCapture cp;
	Mat hsv, hue,mask, hist, backproj;
	CommandLineParser parser(argc, argv, keys);//命令解析器函数
	int cpnum = parser.get<int>("camera");
	cp.open(cpnum);
	if (!cp.isOpened()){
		cout << err_cp;
		parser.printParams();	
		system("pause");
		return -1;
	}
	namedWindow("摄像头扑捉",1);
	Mat frame;
	setMouseCallback("摄像头扑捉",mousecallback);
	int histsize = 16;

	while (1){
		if (!pause)
			cp >> frame;
		if (frame.empty()) break;
		//rectangle(frame,selection,Scalar(0,0,255),2);//测试鼠标
		frame.copyTo(image);
		cvtColor(frame,hsv,CV_BGR2HSV);
		//开始追踪处理
		
		if (trackObject&&!pause){
			inRange(hsv,Scalar(0,10,30),Scalar(180,256,256),mask);
			hue.create(hsv.size(),hsv.depth());
			int ch[] = {0,0};
			mixChannels(&hsv,1,&hue,1,ch,1);
			//对selection的处理
			if (trackObject<0){
				Mat roi(hue, selection), maskroi(mask, selection);
				calcHist(&roi,1,channels,maskroi,hist,1,&histsize,&hranges);
				normalize(hist,hist,0,255,CV_MINMAX);
				histimg = Scalar::all(0);
				Mat color(1,histsize,CV_8UC3);//设定颜色板,显示ROI的Hist
				for (int i = 0; i < histsize; i++){
					color.at<Vec3b>(i) = Vec3b(saturate_cast<int>(hist.at<float>(i)*180/255), 255, 255);//HSV
				}
				cvtColor(color,color,CV_HSV2BGR);
                               //显示ROI的直方图
                                for (int i = 0; i < histsize; i++){
					int val = saturate_cast<int>( hist.at<float>(i)*histimg.cols/180);
					int w = histimg.rows / histsize;
					rectangle(histimg,Point(i*w,histimg.cols-val),Point((i+1)*w,histimg.cols),Scalar(color.at<Vec3b>(i)),-1);

				}


				trackWindow = selection;
				trackObject = 1;

			}
			calcBackProject(&hue,1,channels,hist,backproj,&hranges);
			backproj &= mask;
			meanShift(backproj,trackWindow,TermCriteria(CV_TERMCRIT_EPS|CV_TERMCRIT_ITER,10,1));
			rectangle(image, trackWindow, Scalar(0, 0, 255), 2);

		}
		//没有选定矩形区域,只显示视频

		if (selectObject && selection.width > 0 && selection.height > 0)//鼠标选取时的阴影
		{
			//Mat roi(image, selection);
			//bitwise_not(roi, roi);//bitwise_not为将每一个bit位取反
			rectangle(image, selection, Scalar(0, 0, 255), 2);
		}
		
		
		imshow("摄像头扑捉",image);
		if (showHist) {
			namedWindow("ROI直方图", 1);
			imshow("ROI直方图", histimg);
		}
		else
			destroyWindow("ROI直方图");
		
		char c=waitKey(33);
		switch (c){
		case 'q':
			return 0;
			break;
		case 'h':
			showHist = !showHist;
			break;
		case 'p':
			pause = !pause;
		case 'c':
			trackObject = 0;
			histimg = Scalar::all(0);
			break;
			
		}
		
	}

}
运行截图

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值