meanshift算法学习(一):opencv中的calcBackProject

0.前言

       在看《opencv2计算机视觉编程手册》的第四章时,看到了书中利用opencv提供的meanshift算法实现指定区域的跟踪,感觉很神奇,就相对深入的了解了下。不过这里没有直接上来讲meanshift,而是opencv的calcBackProject()函数。为啥呢,因为书中的例程首先利用它计算反投影矩阵用作meanshift算法的输入。

1.反投影直方图及原理

       直方图是图像的一个重要内容,可以作为图像的一个描述特征(当该图像有明显的纹理时效果更佳)。这里我们有图像B和图像A且图像B中包含图像A或类似于图像A的区域,那么如何确定它在B中的确切位置?一种做法就是,计算图像A的直方图,且假定它能够有效地代表A(不是适用于所有情况),之后遍历图像B,使用B中每一点像素对应A的直方图中的统计值来替换原像素的值。如此一来,图像B就变成 了相对于A中各个成分的一个分布图,每一点的值越高,就代表他它属于图像A的可能性越大。

2.反投影的代码实现

      知道原理后,就可以用代码实现它了,自己写了一个demo来简单的验证,代码如下。

#include <iostream>
#include <vector>
#include <core/core.hpp>
#include <imgproc/imgproc.hpp>
#include <highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <features2d/features2d.hpp>
#include <legacy/legacy.hpp>

using namespace std;
using namespace cv;

int main()
{
	Mat image = imread("beach.jpg", 0);
	Mat image_show;
	image.copyTo(image_show);
	rectangle(image_show, Rect(360, 55, 40, 50), Scalar(0));
	imshow("image", image_show);

	// 选取感兴趣区域
	Mat ROI = image(Rect(360, 55, 40, 50));

	int histSize[1];
	float hranges[2];
	const float* ranges[1];
	int channels[1];

	histSize[0] = 256;
	hranges[0] = 0.f;
	hranges[1] = 255.f;
	ranges[0] = hranges;
	channels[0] = 0;

	// 计算感兴趣区域的灰度直方图
	MatND hist;
	calcHist(&ROI, 1, channels, Mat(), hist, 1, histSize, ranges);
	normalize(hist, hist, 1.0);

	/*Mat result;
	calcBackProject(&image, 1, channels, hist, result, ranges, 255.0);
	imshow("result", result);*/

	double min, max;
	minMaxLoc(hist, &min, &max);

	double scale = 255.f/(max - min);
	Mat result_test(image.size(), CV_8UC1);
	for(int i = 0; i < result_test.rows; i++)
	{
		for(int j = 0; j < result_test.cols; j++)
		{
			int gray = image.at<unsigned char>(i,j);
			// 使用直方图中的统计值相对大小代替原图的灰度值
			result_test.at<unsigned char>(i,j) = (int)((hist.at<float>(gray) - min)*scale);
		}
	}
	imshow("result_test", result_test);

	waitKey();
	return 0;
}
       运行效果如下所示,图1中黑色边框是我们的感兴趣区域,图2则是代表感兴趣区域可能出现的可能性表现(颜色越深,可能性越小)。结果我们看到有很多高可能性的区域,部分原因就是因为黑色边框选择的区域在原图中不具有什么代表性。

图1

图2

3.calcBackProject()函数

       当然上一部分中的反投影计算部分局限性很大,毕竟图像的类型、深度、通道数有很多。但是opencv毕竟是大牛们的作品,opencv提供的calcBackProject()函数能够实现多种类型图像的反投影计算,原理和第二部分大致相同。具体使用我就不提了,书上和网上有很多例程,贴一张用calcBackProject()计算得到的反投影效果,见图3。可以看到和图2相比,区别就在于灰度的分布范围,这个是可以人为控制的。

图3

4.提升反投影效果

       图2或者图3中的效果并不理想,我们拿到后很难确定感兴趣区域的具体位置。部分原因是我们把彩色图转为了灰度图进行的后续计算,丢失了一些色彩信息。针对这个,书中提了一种改进方法,针对彩色图,转换到HSV空间,在计算直方图和反投影时,忽略低饱和度(S)的像素。书中提供了检测猴子脸部的例子,自己比较了下该方法和原始方法的效果差异,是有比较明显的提升。图4中寻找左边猴子脸部在第二张图片的位置,"result"和"result_hsv"分别是使用基本方法(原图转为灰度图后直接计算反投影)和使用书中例程计算得到的结果,对比可以发现后者效果明显好很多,后续使用它作为输入进行位置的精确计算时难度会下降很多。

5.其他

       至此,我们只得到了猴子脸部的一张“分布概率”图,并不知道它的确切位置。那么如何得到最后的位置呢?当然我可以遍历这样图得到最大值的点,然后把该点作为结果。方法简单粗暴,但是复杂度并不好。然后meanshift算法就出现了,它就可以实现结果的快速迭代优化而不是傻兮兮的遍历。终于讲到这了。。不过有关这方面的内容,我要放到下一篇文章了点击打开链接

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mega_Li

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值