图像直方图比较,就是计算两幅图像的直方图数据,比较两组数据的相似性,从而得到两幅图像之间的相似程度,直方图比较在早期的CBIR中是应用很常见的技术手段,通常会结合边缘处理、词袋等技术一起使用。其实现原理为:对输入的两张图像进行计算得到直方图H1与H2,将它们归一化到相同的尺度空间,然后通过计算H1与H2之间的距离得到两个直方图的相似程度进而比较图像本身的相似程度。OpenCV中提供的比较方法有四种:
进行直方图比较的步骤如下:
CV_EXPORTS_W double compareHist( InputArray H1, InputArray H2, int method );
其中,
直方图比较的测试效果如下图所示:
这里我使用的是 Correlation 比较的方法,该方法对比较的两张图像有如下定义:-1 表示完全不匹配、0表示无关联、1表示完全匹配。从图中可以看出数字5的两幅图片匹配度很高为1,表示这两幅图几乎完全相似,而lena人像与猫的图像的匹配度很接近0,说明这两幅图像没有关联性。
图像直方图的反向投影是一种首先寻找某一特征的直方图模型,然后根据这个模型去寻找图像中是否存在这个特征的解决方案。反向投影储存的亮度值,代表测试图像中该像素属于某个特征的概率,也就是说,亮度值相同的位置,属于同一个特征的概率越大,亮起的地方概率更大,内部和边缘之间的阴影影响了检测的精度。反向投影的作用是在输入图像中寻找特定图像中最匹配的点或者区域,也就是定位模版图像在输入图像的位置。投影的结果以每个输入图像像素为起点的直方图对比结果,可以看作是单通道浮点型图像,或者是一个二维的概率数组集合。
简单理解如下:
假设一张 5 × 5 图像的灰度图矩阵为
I m a g e = [ 0 1 2 3 4 4 5 6 7 8 8 9 10 11 12 8 9 3 1 14 ] Image= \left[ \begin{matrix} 0 & 1 & 2 & 3 & 4 \\ 4 & 5 & 6 & 7 & 8 \\ 8 & 9 & 10 & 11 & 12 \\ 8 & 9 & 3 & 1 & 14 \end{matrix} \right] Image=⎣⎢⎢⎡048815992610337111481214⎦⎥⎥⎤
假设 bin 指定的区间为[0,3],[4,7],[8,11],[12,15] 则该 5×5 图像灰度图的直方图为
H i s t o g r a m = [ 6 5 7 2 ] Histogram= \left[ \begin{matrix} 6 & 5 & 7 & 2 \\ \end{matrix} \right] Histogram=[6572]
该 5×5 图像的反向投影图为
B a c k P r o j e c t i o n = [ 6 6 6 6 5 5 5 5 5 7 7 7 7 7 2 7 7 6 6 2 ] Back_Projection= \left[ \begin{matrix} 6 & 6 & 6 & 6 & 5 \\ 5 & 5 & 5 & 5 & 7 \\ 7 & 7 & 7 & 7 & 2 \\ 7 & 7 & 6 & 6 & 2 \end{matrix} \right] BackProjection=⎣⎢⎢⎡65776577657665765722⎦⎥⎥⎤
例如位置(0,0)上的像素值为0,对应的bin为[0,3],所以反向直方图在该位置上的值这个bin的值6。可以看出,反向投影实际上是将原图像的256个灰度值替换为直方图各bin中对应的值,具体值为多少要看把0~255划分为多少个区间!这里只划分了4个bin。反向投影矩阵中某点的值就是它对应的原图像中的点所在区间的灰度直方图值。所以我们可以看出,一个区间点越多,在反向投影矩阵中就越亮。
反向投影在OpenCV中的API函数为:
CV_EXPORTS void calcBackProject( const Mat* images, int nimages,
const int* channels, InputArray hist,
OutputArray backProject, const float** ranges,
double scale = 1, bool uniform = true );
其中,
从一幅图像中寻找和模版最相似的部分的技术叫做模版匹配,但并不是基于直方图的匹配技术,而是类似图像卷积操作一样,通过在输入图像上滑动模板图像,对实际的图像块和输入图像进行匹配的一种匹配方法。
OpenCV中的API函数为:
CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ,
OutputArray result, int method, InputArray mask = noArray() );
其中,
cv::TM_CCOEFF:相关性系数匹配方法,该方法使用源图像与其均值的差、模板与其均值的差二者之间的相关性进行匹配,最佳匹配结果在值等于1处,最差匹配结果在值等于-1处,值等于0直接表示二者不相关。
cv::TM_CCOEFF_NORMED:归一化的相关性系数匹配方法,正值表示匹配的结果较好,负值则表示匹配的效果较差,也是值越大,匹配效果也好。
// HistogramCompare.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include
#include
using namespace cv;
using namespace std;
//直方图比较
void HistogramCompare(void)
{
Mat src1 = imread("figure1.jpg");
Mat src2 = imread("figure2.jpg");
Mat src3 = imread("human.jpg");
Mat src4 = imread("cat.jpg");
namedWindow("input1", WINDOW_AUTOSIZE);
namedWindow("input2", WINDOW_AUTOSIZE);
namedWindow("input3", WINDOW_AUTOSIZE);
namedWindow("input4", WINDOW_AUTOSIZE);
imshow("input1", src1);
imshow("input2", src2);
imshow("input3", src3);
imshow("input4", src4);
Mat hsv1, hsv2, hsv3, hsv4;
cvtColor(src1, hsv1, COLOR_BGR2HSV);
cvtColor(src2, hsv2, COLOR_BGR2HSV);
cvtColor(src3, hsv3, COLOR_BGR2HSV);
cvtColor(src4, hsv4, COLOR_BGR2HSV);
int h_bins = 60; int s_bins = 64;
int histSize[] = { h_bins, s_bins };
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
int channels[] = { 0, 1 };
Mat hist1, hist2, hist3, hist4;
calcHist(&hsv1, 1, channels, Mat(), hist1, 2, histSize, ranges, true, false);
calcHist(&hsv2, 1, channels, Mat(), hist2, 2, histSize, ranges, true, false);
calcHist(&hsv3, 1, channels, Mat(), hist3, 2, histSize, ranges, true, false);
calcHist(&hsv4, 1, channels, Mat(), hist4, 2, histSize, ranges, true, false);
normalize(hist1, hist1, 0, 1, NORM_MINMAX, -1, Mat());
normalize(hist2, hist2, 0, 1, NORM_MINMAX, -1, Mat());
normalize(hist3, hist3, 0, 1, NORM_MINMAX, -1, Mat());
normalize(hist4, hist4, 0, 1, NORM_MINMAX, -1, Mat());
double src1_src2 = compareHist(hist1, hist2, HISTCMP_CORREL);
double src3_src4 = compareHist(hist3, hist4, HISTCMP_CORREL);
printf("Method_HISTCMP_CORREL: src1_src2: %f, src3_src4: %f, \n", src1_src2, src3_src4);
}
//反向投影
void BackProjection(void)
{
Mat srcImage = imread("cat.jpg");
Mat hsvImage, backProjImage;
cvtColor(srcImage, hsvImage, COLOR_BGR2HSV);
int h_bins = 32; int s_bins = 32;
int histSize[] = { h_bins, s_bins };
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
int channels[] = { 0, 1 };
Mat hist;
calcHist(&hsvImage, 1, channels, Mat(), hist, 2, histSize, ranges, true, false);
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
calcBackProject(&hsvImage, 1, channels, hist, backProjImage, ranges, 1.0);
namedWindow("src", WINDOW_AUTOSIZE);
namedWindow("backProjection", WINDOW_AUTOSIZE);
imshow("src", srcImage);
imshow("backProjection", backProjImage);
}
//模板匹配
void TemplateMatch(void)
{
Mat SrcImage, TemplateImage, ResultImage;
SrcImage = imread("cat.jpg");
TemplateImage = imread("Template.jpg");
matchTemplate(SrcImage, TemplateImage, ResultImage, TM_CCOEFF_NORMED);
normalize(ResultImage, ResultImage, -1, 1, NORM_MINMAX);
double MinVal = 0, MaxVal = 0;
Point MinLoc(0, 0), MaxLoc(0, 0);
minMaxLoc(ResultImage, &MinVal, &MaxVal, &MinLoc, &MaxLoc);
circle(SrcImage, Point(MaxLoc.x + TemplateImage.cols / 2, MaxLoc.y + TemplateImage.rows / 2), 30, Scalar(0, 0, 255), 2, 8);
namedWindow("src", WINDOW_AUTOSIZE);
namedWindow("Template", WINDOW_AUTOSIZE);
namedWindow("Result_CCOEFF_NORMED", WINDOW_AUTOSIZE);
imshow("src", SrcImage);
imshow("Template", TemplateImage);
imshow("Result_CCOEFF_NORMED", ResultImage);
}
//获取ROI图片
void GetROIImage(void)
{
Mat SrcImage = imread("cat.jpg");
int Height = SrcImage.rows;
int Width = SrcImage.cols;
Rect rect(Width / 2 - 40, Height / 6 - 40, 100, 100);
Mat ROIImage = SrcImage(rect);
//ROIImage.setTo(Scalar(50, 0, 0));
namedWindow("src", WINDOW_AUTOSIZE);
namedWindow("roi", WINDOW_AUTOSIZE);
imshow("roi", ROIImage);
imshow("src", SrcImage);
//imwrite("Template.jpg", ROIImage);
}
int main(int artc, char** argv)
{
//GetROIImage();
//HistogramCompare();
//BackProjection();
TemplateMatch();
while (true)
{
if (waitKey(10) == 27)
break;
}
destroyAllWindows();
return 0;
}