- 原理
OpenCV中直方图反向投影算法实现来自一篇论文《Indexing Via Color Histograms》其作者有两位,分别是Michael.J.Swain与Dana H. Ballard。论文分为两个部分,其中前面一部分详细描述了颜色直方图以及通过颜色直方图交叉来实现对象鉴别。可以实现对象背景区分、复杂场景中查找对象、不同光照条件影响等。假设M表示模型直方图数据,I表示图像直方图数据,直方图交叉匹配可以被描述为如下:
其中j表示直方图的范围,即bin的个数。最终得到 的结果是表示多少个模型颜色像素与图像中的像素相同或者相似,值越大,表示越相似。归一化表示如下:
这种方法对背景像素变换可以保持稳定性,同时对尺度变换也有一定抗干扰作用,但是无法做到尺度不变性特征。基于上述理论,两位作者发现通过该方法可以定位图像中已知物体的位置,这种方法叫做直方图反向投影(Back Projection)。
2.实例
设有灰度图像矩阵:
将灰度值划分为如下四个区间:
[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的直方图反向投影矩阵为:
通过反向投影,原图像的256个灰度值被反向投影为很少的几个值,具体值的个数要看把0~255划分为多少个区间。反向投影矩阵中某点的值就是它对应的原图像中的点所在区间的灰度直方图的值。可以看出,一个灰度区间所包含的点越多,在反向投影矩阵中就越亮;从上述过程也可以看出,先求出原图像的直方图,再由直方图得到反向投影矩阵,由直方图到反向投影矩阵实际上就是一个反向的过程,所以叫反向。
通过图像的反向投影矩阵,实际上把原图像简单化了,简单化的过程实际上就是提取出图像的某个特征。所以可以用这个特征来对比两幅图,如果两幅图像的反向投影矩阵相似或相同,那么就可以判定这两幅图这个特征是相同的。正是因为直方图反向投影有这样能力,所以在经典的MeanShift与CameraShift跟踪算法中一直是通过直方图反向投影来实现已知对象物体的定位。
3.OpenCV中反向投影的实现
反向投影寻找目标的过程可以简单描述如下:
1) 拿到特征图像(或模板图像)
2) 得到特征图像的直方图
3) 拿到源图像,依据源图像的每个像素的值,在特征图像的直方图中找到对应的值,然后将直方图的值赋给新的图像,backproject算法就完成了。
OpenCV中采用calcBackProject来计算方向直方图,函数原型如下:
void cv::calcBackProject(
const Mat* images, //有相同大小和深度的源图像数组
int nimages,//源图像的数量
const int* channels, //计算反向投影的通道列表
InputArray hist,//输入直方图,即目标区域直方图
//目标反向投影数组,与images[0]大小、深度相同的单通道数组
OutputArray backProject,
const float** ranges,//每个维度的直方图bin边界数组。
double scale = 1, //输出反向投影的可选比例因子。
bool uniform = true //直方图是否均匀的标志。
);
利用反向投影,可以很方便地将前景从相差较大的背景图像中分离出来,代码如下所示:
反向投影代码
void histBackprojection(std::string strPicPath)
{
Mat hist, backproj,hsv,hue;
Mat src = imread(strPicPath, CV_LOAD_IMAGE_COLOR);
// 转换到 HSV 空间
cvtColor(src, hsv, CV_BGR2HSV);
//分离 Hue 通道
hue.create(hsv.size(), hsv.depth());
int ch[] = { 0, 0 };
//将HSV空间中的图像第1个分量复制到hue变量中
mixChannels(&hsv, 1, &hue, 1, ch, 1);
int histSize = 60;
float hue_range[] = { 0, 180 };
const float *ranges = { hue_range };
//计算色调分量图像的直方图
calcHist(&hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false);
//将直方图bin的数值归一化到0-255
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
//计算反向投影
calcBackProject(&hue, 1, 0, hist, backproj, &ranges, 1, true);
//显示反向投影图
namedWindow("backprogection", WINDOW_AUTOSIZE);
imshow("backprogection", backproj);
// 显示直方图
int w = 512; int h = 200;
int bin_w = cvRound((double)w / histSize);
Mat histImg = Mat::zeros(h, w, CV_8UC3);
for (int i = 0; i < histSize; i++){
rectangle(histImg, Point(i*bin_w, h), Point((i + 1)*bin_w, \
h - cvRound(hist.at<float>(i)*h / 255.0)), Scalar(0, 0, \255), -1);
}
imshow("Histogram", histImg);
waitKey(0);
}
如图1的空军基地图像,得到的反向投影图像如图2所示,其直方图如图3所示。从图2中可以看到,只要选择合适的直方图bin数目,蓝色屋顶部分(大片黑色部分)能够很方便地从背景中识别出来。
本文暂时没有评论,来添加一个吧(●'◡'●)