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

来源:互联网 发布:如何学好高中语文知乎 编辑:程序博客网 时间:2024/05/03 14:02

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算法就出现了,它就可以实现结果的快速迭代优化而不是傻兮兮的遍历。终于讲到这了。。不过有关这方面的内容,我要放到下一篇文章了点击打开链接

阅读全文
1 0
原创粉丝点击