图像处理之直方图对比

127 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天

1 概述

对于直方图来说,一个不可或缺的工具便是用某些具体的标准来比较两个直方图的相似度。要对两个直方图(比如说H1和H2)进行比较,首先必须选择一个衡量直方图相似度的对比标准(d(H1, H2))。

在OpenCV中,我们用compareHist()函数来对比两个直方图的相似度,而此函数的返回值就是d(H1, H2)。

2 对比直方图:compareHist()函数

compareHist()函数用于对两幅直方图进行比较。有两个版本的C++原型,如下。

C++:

double compareHist(InputArray H1, InputArray H2, int method)

C++:

double compareHist(const SparseMat& H1, const SparseMat& H2, int method)

它们的前两个参数是要比较的大小相同的直方图,第三个变量是所选择的距离标准。

可采用如下4种方法,比较两个直方图(H1表示第一个,H2表示第二个):

1.相关,Correlation(method=CV_COMP_CORREL)

3.PNG

其中:

4.PNG

且N等于直方图中bin(译为“直条”或“组距”)的个数。

2.卡方,Chi-Square(method=CV_COMP_CHISQR)

5.PNG

3.直方图相交,Intersection(method=CV_COMP_INTERSECT)

6.PNG

4.Bhattacharyya 距离(method=CV_COMP_BHATTACHARYYA)

这里的 Bhattacharyya 距离和 Hellinger 距离相关,也可以写作 method=CV_COMP_HELLINGER

7.PNG

此处的宏定义在当前版本的OpenCV中依然沿用“CV”前缀,在未来版本中应该会有更改。如需使用,可以分别用int类型的1、2、3、4替代CV_COMP_CORREL、CV_COMP_CHISQR、CV_COMP_INTERSECT、 CV_COMP_BHATTACHARYYA 这四个宏。或者“#include<cv.h>”加 入cv.h头文件。

3 示例程序:直方图对比

此次的示例程序为大家演示了如何用compareHist()函数进行直方图对比。代码中的MatND类是用于存储直方图的一种数据结构,用法简单,在这里就不多做讲解,大家看到详细注释的示例程序就会明白。

代码


//---------------------------------【头文件、命名空间包含部分】----------------------------
//		描述:包含程序所使用的头文件和命名空间
//---------------------------------------------------------------------------------------
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;


//--------------------------------------【main( )函数】-----------------------------------------
//          描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//---------------------------------------------------------------------------------------------
int main( )
{
	//【0】改变console字体颜色
	system("color 2F"); 

	//【1】声明储存基准图像和另外两张对比图像的矩阵( RGB 和 HSV )
	Mat srcImage_base, hsvImage_base;
	Mat srcImage_test1, hsvImage_test1;
	Mat srcImage_test2, hsvImage_test2;
	Mat hsvImage_halfDown;

	//【2】载入基准图像(srcImage_base) 和两张测试图像srcImage_test1、srcImage_test2,并显示
	srcImage_base = imread( "1.jpg",1 );
	srcImage_test1 = imread( "2.jpg", 1 );
	srcImage_test2 = imread( "3.jpg", 1 );
	//显示载入的3张图像
	imshow("基准图像",srcImage_base);
	imshow("测试图像1",srcImage_test1);
	imshow("测试图像2",srcImage_test2);

	// 【3】将图像由BGR色彩空间转换到 HSV色彩空间
	cvtColor( srcImage_base, hsvImage_base, CV_BGR2HSV );
	cvtColor( srcImage_test1, hsvImage_test1, CV_BGR2HSV );
	cvtColor( srcImage_test2, hsvImage_test2, CV_BGR2HSV );

	//【4】创建包含基准图像下半部的半身图像(HSV格式)
	hsvImage_halfDown = hsvImage_base( Range( hsvImage_base.rows/2, hsvImage_base.rows - 1 ), Range( 0, hsvImage_base.cols - 1 ) );

	//【5】初始化计算直方图需要的实参
	// 对hue通道使用30个bin,对saturatoin通道使用32个bin
	int h_bins = 50; int s_bins = 60;
	int histSize[] = { h_bins, s_bins };
	// hue的取值范围从0到256, saturation取值范围从0到180
	float h_ranges[] = { 0, 256 };
	float s_ranges[] = { 0, 180 };
	const float* ranges[] = { h_ranges, s_ranges };
	// 使用第0和第1通道
	int channels[] = { 0, 1 };

	// 【6】创建储存直方图的 MatND 类的实例:
	MatND baseHist;
	MatND halfDownHist;
	MatND testHist1;
	MatND testHist2;

	// 【7】计算基准图像,两张测试图像,半身基准图像的HSV直方图:
	calcHist( &hsvImage_base, 1, channels, Mat(), baseHist, 2, histSize, ranges, true, false );
	normalize( baseHist, baseHist, 0, 1, NORM_MINMAX, -1, Mat() );

	calcHist( &hsvImage_halfDown, 1, channels, Mat(), halfDownHist, 2, histSize, ranges, true, false );
	normalize( halfDownHist, halfDownHist, 0, 1, NORM_MINMAX, -1, Mat() );

	calcHist( &hsvImage_test1, 1, channels, Mat(), testHist1, 2, histSize, ranges, true, false );
	normalize( testHist1, testHist1, 0, 1, NORM_MINMAX, -1, Mat() );

	calcHist( &hsvImage_test2, 1, channels, Mat(), testHist2, 2, histSize, ranges, true, false );
	normalize( testHist2, testHist2, 0, 1, NORM_MINMAX, -1, Mat() );


	//【8】按顺序使用4种对比标准将基准图像的直方图与其余各直方图进行对比:
	for( int i = 0; i < 4; i++ )
	{ 
		//进行图像直方图的对比
		int compare_method = i;
		double base_base = compareHist( baseHist, baseHist, compare_method );
		double base_half = compareHist( baseHist, halfDownHist, compare_method );
		double base_test1 = compareHist( baseHist, testHist1, compare_method );
		double base_test2 = compareHist( baseHist, testHist2, compare_method );

		//输出结果
		printf( " 方法 [%d] 的匹配结果如下:\n\n 【基准图 - 基准图】:%f, 【基准图 - 半身图】:%f,【基准图 - 测试图1】: %f, 【基准图 - 测试图2】:%f \n-----------------------------------------------------------------\n", i, base_base, base_half , base_test1, base_test2 );
	}

	printf( "检测结束。" );
	waitKey(0);
	return 0;
}

首先是三张素材图

素材图1

1.jpg

素材图2

2.jpg

素材图3

3.jpg

需要注意的是,在上述代码中还会将基准图像与它自身及其半身图像进行对比。而我们知道,当将基准图像直方图及其自身进行对比时,会产生完美的匹配;当与来源于同一样的背景环境的半身图对比时,应该会有比较高的相似度;当与来自不同亮度光照条件的其余两张测试图像对比时,匹配度应该不是很好。输出的匹配结果如下图所示。

3.PNG

其中的方法0至3,分别表示之前讲过的Correlation、Chi-square、Intersection、 Bhattacharyya 对比标准。其中,对于Correlation(方法0)和Intersection(方法2) 标准,值越大表示相似度越高。

可以发现,【基准图—基准图】的匹配数值结果相对于其他几种匹配方式是最大的,符合实际情况。【基准图一半身图】的匹配结果次大,正如我们预料。而【基准—测试图1】和【基准图—测试图2】的匹配结果却不尽人意,同样和之前的预料吻合。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天