图像的直方图表示图像像素灰度值的统计特性,因此可以通过比较两张图像的直方图特性比较两张图像的相似程度。从一定程度上来讲,虽然两张图像的直方图分布相似不代表两张图像相似,但是两张图像相似则两张图像的直方图分布一定相似。例如通过插值对图像进行放缩后图像的直方图虽然不会与之前完全一致,但是两者一定具有很高的相似性,因而可以通过比较两张图像的直方图分布相似性对图像进行初步的筛选与识别。
OpenCV 4中提供了用于比较两个图像直方图相似性的compareHist()函数,该函数原型在代码清单4-5中给出。
double cv::compareHist(InputArray H1,
InputArray H2,
int method
)
该函数前两个参数为需要比较相似性的图像直方图,由于不同尺寸的图像中像素数目可能不相同,为了能够得到两个直方图图像正确的相识性,需要输入同一种方式归一化后的图像直方图,并且要求两个图像直方图具有相同的尺寸。函数第三个参数为比较相似性的方法,选择不同的方法,会得到不同的相似性系数,函数将计算得到的相似性系数以double类型返回。由于不同计算方法的规则不一,因此相似性系数代表的含义也不相同,函数可以选择的计算方式标志在表4-2中给出,接下来介绍每种方法比较相似性的原理。
该方法名为相关法,其计算相似性原理在式(6.1)中给出,在该方法中如果两个图像直方图完全一致,则计算数值为1;如果两个图像直方图完全不相关,则计算值为0。
其中
其中 N是直方图的灰度值个数。
该方法名为卡方法,其计算相似性原理在式(6.3)中给出,在该方法中如果两个图像直方图完全一致,则计算数值为0,两个图像的相似性越小,计算数值越大。
该方法名为直方图相交法,其计算相似性原理在式(6.4)中给出,在该方法不会将计算结果归一化,因此即使是两个完全一致的图像直方图,来自于不同图像也会有不同的数值,但是其遵循与同一个图像直方图比较时,数值越大相似性越高,数值越小相似性越低。
该方法名为直方图相交法,其计算相似性原理在式(6.4)中给出,在该方法不会将计算结果归一化,因此即使是两个完全一致的图像直方图,来自于不同图像也会有不同的数值,但是其遵循与同一个图像直方图比较时,数值越大相似性越高,数值越小相似性越低。
该方法名为巴塔恰里雅距离(巴氏距离)法,其计算相似性原理在式(6.5)中给出,在该方法中如果两个图像直方图完全一致,则计算数值为0,两个图像的相似性越小,计算数值越大。
该方法与巴氏距离法相同,常用于替代巴氏距离法用于纹理比较,计算公式如式(6.6),
该方法名为相对熵法,又名Kullback-Leibler散度法,其计算相似性原理在式(6.7)中给出,在该方法中如果两个图像直方图完全一致,则计算数值为0,两个图像的相似性越小,计算数值越大。
为了验证通过直方图比较两张图像相似性的可行性,在代码清单4-6中提供了三张图像直方图比较的示例程序。在程序中,我们将读取的图像转成灰度图像,之后将图像缩小为原来尺寸的一半,同时读取另外一张图像的灰度图,计算这三张图像的直方图,直方图的结果在图4-4中给出,通过观看直方图的趋势可以发现即使将图像尺寸缩小,两张图像的直方图分布也有一定的相似性。之后利用compareHist()函数对三个直方图进行比较,比较结果也显示图像缩小后的直方图与原来图像的直方图具有很高的相似性,而两张完全不相同的图像的直方图相似性比较小。
#include
#include
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(){
Mat img=imread("apple.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("lena.png",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;
}