.. _histogram_comparison: 直方图对比 ******************** 目标 ==== 本文档尝试解答如下问题: .. container:: enumeratevisibleitemswithsquare * 如何使用OpenCV函数 :compare_hist:`compareHist <>` 产生一个表达两个直方图的相似度的数值。 * 如何使用不同的对比标准来对直方图进行比较。 原理 ====== .. container:: enumeratevisibleitemswithsquare * 要比较两个直方图( :math:`H_{1}` and :math:`H_{2}` ), 首先必须要选择一个衡量直方图相似度的 *对比标准* (:math:`d(H_{1}, H_{2})`) 。 * OpenCV 函数 :compare_hist:`compareHist <>` 执行了具体的直方图对比的任务。该函数提供了4种对比标准来计算相似度: a. **Correlation ( CV\_COMP\_CORREL )** .. math:: d(H_1,H_2) = \frac{\sum_I (H_1(I) - \bar{H_1}) (H_2(I) - \bar{H_2})}{\sqrt{\sum_I(H_1(I) - \bar{H_1})^2 \sum_I(H_2(I) - \bar{H_2})^2}} 其中 .. math:: \bar{H_k} = \frac{1}{N} \sum _J H_k(J) :math:`N` 是直方图中bin的数目。 b. **Chi-Square ( CV\_COMP\_CHISQR )** .. math:: d(H_1,H_2) = \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)+H_2(I)} c. **Intersection ( CV\_COMP\_INTERSECT )** .. math:: d(H_1,H_2) = \sum _I \min (H_1(I), H_2(I)) d. **Bhattacharyya 距离( CV\_COMP\_BHATTACHARYYA )** .. math:: d(H_1,H_2) = \sqrt{1 - \frac{1}{\sqrt{\bar{H_1} \bar{H_2} N^2}} \sum_I \sqrt{H_1(I) \cdot H_2(I)}} 源码 ==== .. container:: enumeratevisibleitemswithsquare * **本程序做什么?** .. container:: enumeratevisibleitemswithsquare * 装载一张 *基准图像* 和 两张 *测试图像* 进行对比。 * 产生一张取自 *基准图像* 下半部的图像。 * 将图像转换到HSV格式。 * 计算所有图像的H-S直方图,并归一化以便对比。 * 将 *基准图像* 直方图与 两张测试图像直方图,基准图像半身像直方图,以及基准图像本身的直方图分别作对比。 * 显示计算所得的直方图相似度数值。 * **下载代码**: 点击 `这里 `_ * **代码一瞥:** .. code-block:: cpp #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include #include using namespace std; using namespace cv; /** @函数 main */ int main( int argc, char** argv ) { Mat src_base, hsv_base; Mat src_test1, hsv_test1; Mat src_test2, hsv_test2; Mat hsv_half_down; /// 装载三张背景环境不同的图像 if( argc < 4 ) { printf("** Error. Usage: ./compareHist_Demo \n"); return -1; } src_base = imread( argv[1], 1 ); src_test1 = imread( argv[2], 1 ); src_test2 = imread( argv[3], 1 ); /// 转换到 HSV cvtColor( src_base, hsv_base, CV_BGR2HSV ); cvtColor( src_test1, hsv_test1, CV_BGR2HSV ); cvtColor( src_test2, hsv_test2, CV_BGR2HSV ); hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) ); /// 对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 }; /// 直方图 MatND hist_base; MatND hist_half_down; MatND hist_test1; MatND hist_test2; /// 计算HSV图像的直方图 calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false ); normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() ); calcHist( &hsv_half_down, 1, channels, Mat(), hist_half_down, 2, histSize, ranges, true, false ); normalize( hist_half_down, hist_half_down, 0, 1, NORM_MINMAX, -1, Mat() ); calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false ); normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() ); calcHist( &hsv_test2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false ); normalize( hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat() ); ///应用不同的直方图对比方法 for( int i = 0; i < 4; i++ ) { int compare_method = i; double base_base = compareHist( hist_base, hist_base, compare_method ); double base_half = compareHist( hist_base, hist_half_down, compare_method ); double base_test1 = compareHist( hist_base, hist_test1, compare_method ); double base_test2 = compareHist( hist_base, hist_test2, compare_method ); printf( " Method [%d] Perfect, Base-Half, Base-Test(1), Base-Test(2) : %f, %f, %f, %f \n", i, base_base, base_half , base_test1, base_test2 ); } printf( "Done \n" ); return 0; } 解释 =========== #. 声明储存基准图像和另外两张对比图像的矩阵( RGB 和 HSV ) .. code-block:: cpp Mat src_base, hsv_base; Mat src_test1, hsv_test1; Mat src_test2, hsv_test2; Mat hsv_half_down; #. 装载基准图像(src\_base) 和两张测试图像: .. code-block:: cpp if( argc < 4 ) { printf("** Error. Usage: ./compareHist_Demo \n"); return -1; } src_base = imread( argv[1], 1 ); src_test1 = imread( argv[2], 1 ); src_test2 = imread( argv[3], 1 ); #. 将图像转化到HSV格式: .. code-block:: cpp cvtColor( src_base, hsv_base, CV_BGR2HSV ); cvtColor( src_test1, hsv_test1, CV_BGR2HSV ); cvtColor( src_test2, hsv_test2, CV_BGR2HSV ); #. 同时创建包含基准图像下半部的半身图像(HSV格式): .. code-block:: cpp hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) ); #. 初始化计算直方图需要的实参(bins, 范围,通道 H 和 S ). .. code-block:: cpp int h_bins = 50; int s_bins = 32; int histSize[] = { h_bins, s_bins }; float h_ranges[] = { 0, 256 }; float s_ranges[] = { 0, 180 }; const float* ranges[] = { h_ranges, s_ranges }; int channels[] = { 0, 1 }; #. 创建储存直方图的 MatND 实例: .. code-block:: cpp MatND hist_base; MatND hist_half_down; MatND hist_test1; MatND hist_test2; #. 计算基准图像,两张测试图像,半身基准图像的直方图: .. code-block:: cpp calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false ); normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() ); calcHist( &hsv_half_down, 1, channels, Mat(), hist_half_down, 2, histSize, ranges, true, false ); normalize( hist_half_down, hist_half_down, 0, 1, NORM_MINMAX, -1, Mat() ); calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false ); normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() ); calcHist( &hsv_test2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false ); normalize( hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat() ); #. 按顺序使用4种对比标准将基准图像(hist\_base)的直方图与其余各直方图进行对比: .. code-block:: cpp for( int i = 0; i < 4; i++ ) { int compare_method = i; double base_base = compareHist( hist_base, hist_base, compare_method ); double base_half = compareHist( hist_base, hist_half_down, compare_method ); double base_test1 = compareHist( hist_base, hist_test1, compare_method ); double base_test2 = compareHist( hist_base, hist_test2, compare_method ); printf( " Method [%d] Perfect, Base-Half, Base-Test(1), Base-Test(2) : %f, %f, %f, %f \n", i, base_base, base_half , base_test1, base_test2 ); } 结果 ======== #. 使用下列输入图像: ============ ============ ============ |Base_0| |Test_1| |Test_2| ============ ============ ============ .. |Base_0| image:: images/Histogram_Comparison_Source_0.jpg :align: middle .. |Test_1| image:: images/Histogram_Comparison_Source_1.jpg :align: middle .. |Test_2| image:: images/Histogram_Comparison_Source_2.jpg :align: middle 第一张为基准图像,其余两张为测试图像。同时我们会将基准图像与它自身及其半身图像进行对比。 #. 我们应该会预料到当将基准图像直方图及其自身进行对比时会产生完美的匹配, 当与来源于同一样的背景环境的半身图对比时应该会有比较高的相似度, 当与来自不同亮度光照条件的其余两张测试图像对比时匹配度应该不是很好: #. 下面显示的是结果数值: =============== =============== =============== =============== =============== *对比标准* 基准 - 基准 基准 - 半身 基准 - 测试1 基准 - 测试2 =============== =============== =============== =============== =============== *Correlation* 1.000000 0.930766 0.182073 0.120447 *Chi-square* 0.000000 4.940466 21.184536 49.273437 *Intersection* 24.391548 14.959809 3.889029 5.775088 *Bhattacharyya* 0.000000 0.222609 0.646576 0.801869 =============== =============== =============== =============== =============== 对于 *Correlation* 和 *Intersection* 标准, 值越大相似度越大。因此可以看到对于采用这两个方法的对比,*基准 - 基准* 的对比结果值是最大的, 而 *基准 - 半身* 的匹配则是第二好(跟我们预测的一致)。而另外两种对比标准,则是结果越小相似度越大。 我们可以观察到基准图像直方图与两张测试图像直方图的匹配是最差的,这再一次印证了我们的预测。 翻译者 ================= niesu@ `OpenCV中文网站 `_