由于图像的直方图表示图像像素灰度值的统计特性,因此可以通过两幅图像的直方图特性比较 两幅图像的相似程度。从一定程度上来讲,虽然两幅图像的直方图分布相似不代表两幅图像相似,但是两幅图像相似则两幅图像的直方图分布一定相似。例如,在通过插值对图像进行缩放后,虽然图像的直方图不会与之前完全一致,但是两者之间一定具有很高的相似性,因而可以通过比较两幅图像的直方图分布相似性对图像进行初步的筛选与识别。
OpenCV 4 中提供了用于比较两个图像直方图相似性的
compareHist()
函数。
1. compareHist()函数原型
double cv::compareHist(InputArray H1,
InputArray H2,
int method
)
- H1:第一幅图像直方图。
- H2:第二幅图像直方图,与 H1 具有相同的尺寸。
- method:比较方法标志。
该函数前两个参数为需要比较相似性的图像直方图,由于不同尺寸的图像中像素数目可能不相同,为了能够得到两个图像直方图正确的相似性,需要输入同一种方式归一化后的图像直方图,并且要求两个图像直方图具有相同的尺寸。该函数中第三个参数为比较相似性的方法,选择不同的方法会得到不同的相似性系数,会将计算得到的相似性系数以 double
类型返回。由于不同计算方法的规则不一,因此相似性系数代表的含义也不相同。接下来介绍每种方法比较相似性的原理。
compareHist()函数比较直方图方法的可选择标志参数
1.1 HISTCMP_CORREL
该方法名为相关法,
在该方法中,如果两个图像直方图完全一致,那么计算数值为 1
;如果两个图像直方图完全不相关,那么计算值为
0
。
其中,N 是直方图的灰度值个数。
1.2 HISTCMP_CHISQR
该方法名为卡方法,在该方法中,如果两个图像直方图完全一致,那么计算数值为 0;两个图像的相似性越小,计算数值越大。
1.3 HISTCMP_INTERSECT
该方法名为直方图相交法,
该方法不会将计算结果归一化,因此,即使是两个完全一致的图像直方图,来自于不同图像,也会有不同的数值。例如,由A 图像缩放后得到的两个完全一样的直方图相似性结果与由
B
图像缩放后得到的两个完全一样的直方图相似性结果可能不相同。但是,当任意图像的直方图与 A
图像的直方图比较时,数值越大, 相似性越高,数值越小,相似性越低。
1.4 HISTCMP_BHATTACHARYYA
该方法名为巴塔恰里雅距离(巴氏距离)法,
在该方法中,如果两个图像直方图完全一致,那么计算数值为 0
;两个图像的相似性越小,计算数值越大。
1.5 HISTCMP_CHISQR_ALT
该方法称为替代卡方法,其判断两个直方图是否相似的方法与巴氏距离法相同,常用于替代巴氏距离法用于纹理比较。
1.6 HISTCMP_KL_DIV
该方法名为相对熵法,又名 Kullback-Leibler
散度法,
在该方法中,如果两个图像直方图完全一致,那么计算数值为 0
;两个图像的相似性越小,计算数值越大。
2. 示例代码
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void drawHist(Mat &hist, int type, string name) //归一化并绘制直方图函数
{
int hist_w = 512;
int hist_h = 400;
int width = 2;
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
normalize(hist, hist, 1, 0, type, -1, Mat());
for (int i = 1; i <= hist.rows; i++)
{
rectangle(histImage, Point(width*(i - 1), hist_h - 1),
Point(width*i - 1, hist_h - cvRound(hist_h*hist.at<float>(i - 1)) - 1),
Scalar(255, 255, 255), -1);
}
imshow(name, histImage);
}
//主函数
int main()
{
//system("color F0"); //更改输出界面颜色
Mat img = imread("../pic/gril_1.jpg");
if (img.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat gray, hist, gray2, hist2, gray3, hist3;
cvtColor(img, gray, COLOR_BGR2GRAY);
resize(gray, gray2, Size(), 0.5, 0.5);
gray3 = imread("../pic/hand.jpg", IMREAD_GRAYSCALE);
const int channels[1] = { 0 };
float inRanges[2] = { 0,255 };
const float* ranges[1] = { inRanges };
const int bins[1] = { 256 };
calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);
calcHist(&gray2, 1, channels, Mat(), hist2, 1, bins, ranges);
calcHist(&gray3, 1, channels, Mat(), hist3, 1, bins, ranges);
drawHist(hist, NORM_INF, "hist");
drawHist(hist2, NORM_INF, "hist2");
drawHist(hist3, NORM_INF, "hist3");
//原图直方图与原图直方图的相关系数
double hist_hist = compareHist(hist, hist, HISTCMP_CORREL);
cout << "apple_apple=" << hist_hist << endl;
//原图直方图与缩小原图后的直方图的相关系数
double hist_hist2 = compareHist(hist, hist2, HISTCMP_CORREL);
cout << "apple_apple256=" << hist_hist2 << endl;
//两幅不同图像直方图相关系数
double hist_hist3 = compareHist(hist, hist3, HISTCMP_CORREL);
cout << "apple_lena=" << hist_hist3 << endl;
waitKey(0);
return 0;
}
3. 测试结果