直方图的反向投影原理 及 代码实现
要理解直方图的反向投影,先要看下直方图反向投影矩阵的计算方法!
设有原灰度图像矩阵:
Image=
1 2 3 4
5 6 7 7
9 8 0 1
5 6 7 6
将灰度值划分为如下四个区间:
[0,2] [3,5] [6,7] [8,10]
很容易得到这个图像矩阵的直方图hist= 4 4 6 2
好,接下来计算反向投影矩阵:
反向投影矩阵的大小和原灰度图像矩阵的大小相同!
原图像中坐标为(0,0)的灰度值为1,1位于区间[0,2] 中,区间[0,2] 对应的直方图值为4,所以反向投影矩阵中中坐标为(0,0)的值记为4
按上面的计算方法,可以得到Image的直方图反向投影矩阵为:
back_Projection=
4 4 4 4
4 6 6 6
2 2 4 4
4 6 6 6
从上面的计算过程我们来理解直方图的反向投影到底代表什么?
我们看到,实际上是原图像的256个灰度值被置为很少的几个值了,具体有几个值,要看把0~255划分为多少个区间!反向投影矩阵中某点的值就是它对应的原图像中的点所在区间的灰度直方图值。所以我们可以看出,一个区间点越多,在反向投影矩阵中就越亮。
那么怎么理解反向投影矩阵中的“反向”二字呢?从这个过程可以看出,我们是先求出原图像的直方图,再由直方图得到反向投影矩阵,由直方图到反向投影矩阵实际上就是一个反向的过程,所以叫反向。
通过图像的反向投影矩阵,我们实际上把原图像简单化了,简单化的过程实际上就是提取出图像的某个特征。所以以后我们可以用这个特征来对比两幅图,如果两幅图的反向投影矩阵相似或相同,那么我们就可以判定这两幅图这个特征是相同的。
//==========================华丽的分割线========================//
如果一幅图像的区域中显示的是一种结构纹理或者一个独特的物体,那么这个区域的直方图可以看作一个概率函数,他给的是某个像素属于该纹理或物体的概率。
所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找测试图像中存在的该特征。
利用Hue直方图解释反向投影原理:
1、获取测试图像中每个像素的hue数据 hi,j,并找到 hi,j 在hue直方图中的bin的位置。
2、查询hue直方图中对应bin的数值。
3、将该数值存储在新的图像中(BackProjection),也可以先归一化hue直方图数值到0-255范围,这样可以直接显示BackProjection图像(单通道图像)。
4、通过对测试图像每个像素采取以上步骤,可以得到最终的BackProjection图像。
代码如下:
backprojection.h
#ifndef BACKPROJECTION_H #define BACKPROJECTION_H #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> //全局函数///////////////////////////////// void hist_and_backprojection(int, void* ); #endif // BACKPROJECTION_H
backprojection.cpp
#include "backprojection.h" extern cv::Mat src, hsv, hue; extern int bins; void hist_and_backprojection(int, void* ) { cv::MatND hist; //直方图bin的数目大小 int histSize = cv::max(bins, 2); float hue_range[] = {0, 180}; const float *ranges = { hue_range }; //计算直方图并归一化 cv::calcHist(&hue, 1, //图像数量 0, //通道数 cv::Mat(), //不使用掩膜 hist, 1, //直方图维度 &histSize, //每一维直方图bin的数目 &ranges, //每一维直方图的范围 true, false ); //将直方图bin的数值归一化到0-255,可方便直接显示反向投影图 cv::normalize(hist, hist, 0, 255, cv::NORM_MINMAX, -1, cv::Mat()); //计算反向投影 cv::MatND backproj; cv::calcBackProject(&hue, 1, //源图像的数目 0, //用于计算反向投影值的通道列表 hist, //输入直方图 backproj, //单通道反向投影图像 &ranges, //每一维直方图bin的范围 1, true); //显示反向投影图 cv::namedWindow("BackProjection"); cv::imshow("BackProjection", backproj); //显示直方图 int w = 400, h = 400; int bin_w = cvRound((double)w / histSize); cv::Mat histImg = cv::Mat::zeros( w, h, CV_8UC3 ); for(int i = 0; i < bins; i++) { cv::rectangle(histImg, cv::Point(i*bin_w, h), cv::Point((i+1)*bin_w, h - cvRound(hist.at<float>(i) * h / 255.0) ), cv::Scalar(0, 0, 255), -1); } cv::namedWindow("Histogram"); cv::imshow("Histogram", histImg); }
main.cpp
#include <QtCore/QCoreApplication> #include "backprojection.h" //全局变量///////////////////////////////// cv::Mat src, hsv, hue; int bins = 25; int main(int argc, char *argv[]) { // QCoreApplication a(argc, argv); // return a.exec(); src = cv::imread("../image/HandIndoorColor.jpg", 1); //转换到HSV cv::cvtColor(src, hsv, cv::COLOR_RGB2HSV); cv::namedWindow("hsvImg"); cv::imshow("hsvImg", hsv); //分离Hue通道(即色相通道) hue.create(hsv.size(), hsv.depth()); int ch[] = {0, 0}; cv::mixChannels(&hsv, 1, &hue, 1, ch, 1); //创建Trackbar来输入bin的数目 const char *window_image = "Source image"; cv::namedWindow(window_image, cv::WINDOW_AUTOSIZE); cv::createTrackbar("* Hue bins: ",window_image, &bins, 180, hist_and_backprojection //回调函数 ); hist_and_backprojection(0, 0); cv::imshow(window_image, src); //等待用户反应 cv::waitKey(0); return 0; }
运行结果图:
滑动条用于调节直方图bin的数目。
HSV颜色空间图像:
调节不同bin的数目对应的Hue直方图:
调节不同bin的数目对应的反向投影(BackProjection)图像:
参考博客:
http://www.cnblogs.com/liu-jun/p/3482211.html
http://lib.csdn.net/article/opencv/28580