本文为学习《OpenCV计算机视觉编程攻略(第二版)》4.6节,均值漂算法查找目标的内容笔记。

直方图反向投影的结果是一个概率分布图,表示一个指定图像片段出现在特定位置的概率。假设我们已经知道图像中某个物体的大致位置,就可以用概率分布图找到物体的准确位置。最可能出现的位置就是窗口中概率最大的位置。如果从一个可能的初始位置开始,在该位置周围反复移动,就可能找到物体所在的准确位置,这个实现方法称为均值漂移算法Mean Shift。

MeanShift算法在视频跟踪领域应用广泛,最早由D.Comaniciu和P.Meer于2002年在P.A.M.I上首次提出:Mean Shift: A robust approachtoward feature space analysis[J], IEEE Trans. on Pattern Analysis and MachineIntellligence, 2002,5(24).

函数 cv::meanShift 在给定反向投影和初始搜索窗口位置的情况下,用迭代方法寻找目标中心。当搜索窗口中心的移动小于某个给定值时或者函数已经达到最大迭代次数时停止迭代。 函数返回迭代次数。

函数形式:

int meanShift( InputArray probImage, CV_OUT CV_IN_OUT Rect& window, TermCriteria criteria );

参数说明:

  • probImage:概率分布图像,可以是目标直方图的反向投影(见 cvCalcBackProject)
  • Window:初始搜索窗口,可以是使用Rect定义ROI
  • Criteria:确定窗口搜索停止的准则,OpenCV实现该算法时定义了两个停止条件:迭代次数达到设置的最大值;窗口中心的漂移值小于某个设定的限值。

 

1.添加头文件

新建一个win32控制台程序meanShift.sln.将前面博文“OpenCV学习笔记-反向投影直方图检测特定图像内容”中的两个头文件拷贝到meanShift工程目录下,并在源文件meanShift.cpp中添加对它们的包含
#include "colorhistogram.h"   //彩色直方图头文件
#include "contentFinder.h"    //反向投影直方图检测头文件

上述两个头文件中定义了分别用于获取彩色直方图的类ColorHistogram,以及进行直方图反向投影的类ContentFinder,这两个类在本例中都需要使用。

其他需要用到的头文件包括:
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <highgui.h>
#include <opencv2/video/tracking.hpp>  //meanShift算法所在头文件

2.实现步骤

程序的实现步骤包括:

 

  • 读入参考图像
  • 设置感兴趣区域(ROI)
  • 获取感兴趣区域的色调直方图
  • 读取新图像计算色调直方图的反向投影:新建直方图反向投影类实例,将色调直方图输入该实例
  • 使用meanShift算法在反向投影图中定位物体:设置初始搜索区域,设置迭代终止条件

 

(1) 读入参考图像

// Read reference image
	cv::Mat image= cv::imread("f:\\images\\neo1.jpg");//图像路径自定义
	if (!image.data)
		return 0; 

(2)设置感兴趣区域

// Define ROI
	cv::Mat imageROI= image(cv::Rect(445,50,148,216));//neo
	cv::rectangle(image, cv::Rect(445,50,148,216),cv::Scalar(0,0,255));//draw red rectangle
// Display image (可根据图像尺寸选择显示的比例,以下为原尺寸显示)
	cv::Mat window(image, cv::Rect(0, 0, image.cols, image.rows));//用于修改图像显示比例
	cv::namedWindow("Image");
	cv::imshow("Image",window);

参考图像及感兴趣区域

(3)获取感兴趣区域的色调直方图

彩色图像中,为了突出被寻找物体的特征,可根据实际选择使用HSV色彩空间的色调分量(即颜色)。Neo脸部颜色与环境有明显不同,使用色调比价容易标识出脸部。因此,需要把图像转换成HSV色彩空间。使用cv::cvtColort函数的CV_BGR2HSV转换标志,转换后得到的第一个通道就是色调通道。这是一个8位分量,值的范围是0~180.
值得注意的是,在使用色调分量时,要把图像像素的饱和度考虑在内(饱和度为第2个通道)。因为,如果颜色饱和度过低,它的色调信息就会变得不稳定且不可靠,这是因为低饱和度的颜色的BGR分量几乎是相等的。这导致很难确定它们所表示的准确颜色。程序的做法是设置一个饱和度阈值minSat,饱和度低于该阈值的像素将被屏蔽。
// Get the Hue histogram
	int minSat=65;
	ColorHistogram hc; //调用彩色直方图获取方法,该方法中定义了一个获取HueHistogram的方.getHueHistogram
	cv::Mat colorhist= hc.getHueHistogram(imageROI,minSat);//获取HSV彩色模式1维色调直方图,minSat不为0时设置饱和度阈值minSat

(4)读取新图像计算色调直方图的反向投影

<span style="white-space:pre">	</span>ContentFinder finder;
	finder.setHistogram(colorhist);
	finder.setThreshold(0.2f);
 
// Convert to HSV space (just for display)
	cv::Mat hsv;
	cv::cvtColor(image, hsv, CV_BGR2HSV);
 
// Split the image将HSV图像的3个通道分割进3个图像v[0],v[1],v[2]
	vector<cv::Mat> v;
	cv::split(hsv,v);
 
// Eliminate pixels with low saturation
	cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);//根据饱和度进行阈值分割,HSV色彩空间的第2个通道v[1]为饱和度通道
	cv::namedWindow("Saturation mask");
	cv::imshow("Saturation mask",v[1]);//显示阈值分割后的饱和度蒙板
 
//--------------
// Second image读取第2幅图像
	image= cv::imread("f:\\images\\neo2.jpg");
 
// Display image (可根据需要调整显示比例)
	cv::Mat window2(image, cv::Rect(0, 0, image.cols, image.rows));//此处可修改显示比例
	cv::namedWindow("Image 2");
	cv::imshow("Image 2",window2);
	waitKey();// Convert to HSV space
	cv::cvtColor(image, hsv, CV_BGR2HSV);
 
// Get back-projection of hue histogram计算色调直方图的反向投影
	int ch[1]={0};
	finder.setThreshold(-1.0f); // no thresholding
	cv::Mat result= finder.find(hsv,0.0f,180.0f,ch);
 
// Display back projection result
	cv::namedWindow("Backprojection on second image");
	cv::imshow("Backprojection on second image",result);

设置饱和度阈值minSat,低于该阈值的饱和度像素将被屏蔽,由此获得一个mask掩码图像,如下图所示,白色部分为饱和度大于阈值的像素,黑色部分为饱和度低于阈值的部分。

原图的饱和度蒙版

第2幅图像

第2幅图像的色调直方图反向投影图(概率分布图:ProbImage)

(5)用meanShift算法在反向投影图中定位物体

// initial window position
	cv::Rect rect(445,50,148,216);//neo
	cv::rectangle(image, rect, cv::Scalar(0,0,255));
 
// search objet with mean shift
	cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);//迭代停止条件为:最大迭代10次,中心偏移距离小于0.01
	cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;
 
// draw output window
	cv::rectangle(image, rect, cv::Scalar(0,255,0));//draw Green rectangle in the second image
 
// Display image
	cv::Mat window2r(image, cv::Rect(0, 0, image.cols, image.rows));
	cv::namedWindow("Image 2 result");
	cv::imshow("Image 2 result",window2r);
 
	cv::waitKey();

定位结果红色框为原图中neo的位置,绿色框为新图像中的位置

 

转载请注明:iracer的CSDN博客 http://blog.csdn.net/iracer/article/details/48955151


新书终于面市啦,《机器学习原理与编程实战》连接原理与实战,感兴趣的同学请移步:

https://blog.csdn.net/iracer/article/details/116051674?spm=1001.2014.3001.5501