diff --git a/modules/imgproc/doc/histograms.rst b/modules/imgproc/doc/histograms.rst index 1ec6a0177..2240e0445 100644 --- a/modules/imgproc/doc/histograms.rst +++ b/modules/imgproc/doc/histograms.rst @@ -173,6 +173,8 @@ Compares two histograms. * **CV_COMP_CHISQR** Chi-Square + * **CV_COMP_CHISQR_ALT** Alternative Chi-Square + * **CV_COMP_INTERSECT** Intersection * **CV_COMP_BHATTACHARYYA** Bhattacharyya distance @@ -202,6 +204,14 @@ The functions ``compareHist`` compare two dense or two sparse histograms using t d(H_1,H_2) = \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)} +* Alternative Chi-Square (``method=CV_COMP_CHISQR_ALT``) + + .. math:: + + d(H_1,H_2) = 2 * \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)+H_2(I)} + + This alternative formula is regularly used for texture comparison. See e.g. [Puzicha1997]_. + * Intersection (``method=CV_COMP_INTERSECT``) .. math:: @@ -493,3 +503,4 @@ The function clears histogram bins that are below the specified threshold. .. [RubnerSept98] Y. Rubner. C. Tomasi, L.J. Guibas. *The Earth Mover’s Distance as a Metric for Image Retrieval*. Technical Report STAN-CS-TN-98-86, Department of Computer Science, Stanford University, September 1998. +.. [Puzicha1997] Puzicha, J., Hofmann, T., and Buhmann, J. *Non-parametric similarity measures for unsupervised texture segmentation and image retrieval.* In Proc. IEEE Conf. Computer Vision and Pattern Recognition, San Juan, Puerto Rico, pp. 267-272, 1997. diff --git a/modules/imgproc/include/opencv2/imgproc/types_c.h b/modules/imgproc/include/opencv2/imgproc/types_c.h index 2b1d0722e..dd0d8b8a6 100644 --- a/modules/imgproc/include/opencv2/imgproc/types_c.h +++ b/modules/imgproc/include/opencv2/imgproc/types_c.h @@ -508,7 +508,8 @@ enum CV_COMP_CHISQR =1, CV_COMP_INTERSECT =2, CV_COMP_BHATTACHARYYA =3, - CV_COMP_HELLINGER =CV_COMP_BHATTACHARYYA + CV_COMP_HELLINGER =CV_COMP_BHATTACHARYYA, + CV_COMP_CHISQR_ALT =4 }; /* Mask size for distance transform */ diff --git a/modules/imgproc/src/histogram.cpp b/modules/imgproc/src/histogram.cpp index 08252586a..f0a78187d 100644 --- a/modules/imgproc/src/histogram.cpp +++ b/modules/imgproc/src/histogram.cpp @@ -1990,12 +1990,12 @@ double cv::compareHist( InputArray _H1, InputArray _H2, int method ) const float* h2 = (const float*)it.planes[1].data; len = it.planes[0].rows*it.planes[0].cols*H1.channels(); - if( method == CV_COMP_CHISQR ) + if( (method == CV_COMP_CHISQR) || (method == CV_COMP_CHISQR_ALT)) { for( j = 0; j < len; j++ ) { double a = h1[j] - h2[j]; - double b = h1[j]; + double b = (method == CV_COMP_CHISQR) ? h1[j] : h1[j] + h2[j]; if( fabs(b) > DBL_EPSILON ) result += a*a/b; } @@ -2034,7 +2034,9 @@ double cv::compareHist( InputArray _H1, InputArray _H2, int method ) CV_Error( CV_StsBadArg, "Unknown comparison method" ); } - if( method == CV_COMP_CORREL ) + if( method == CV_COMP_CHISQR_ALT ) + result *= 2; + else if( method == CV_COMP_CORREL ) { size_t total = H1.total(); double scale = 1./total; @@ -2063,13 +2065,13 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method ) CV_Assert( H1.size(i) == H2.size(i) ); const SparseMat *PH1 = &H1, *PH2 = &H2; - if( PH1->nzcount() > PH2->nzcount() && method != CV_COMP_CHISQR ) + if( PH1->nzcount() > PH2->nzcount() && method != CV_COMP_CHISQR && method != CV_COMP_CHISQR_ALT) std::swap(PH1, PH2); SparseMatConstIterator it = PH1->begin(); int N1 = (int)PH1->nzcount(), N2 = (int)PH2->nzcount(); - if( method == CV_COMP_CHISQR ) + if( (method == CV_COMP_CHISQR) || (method == CV_COMP_CHISQR_ALT) ) { for( i = 0; i < N1; i++, ++it ) { @@ -2077,7 +2079,7 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method ) const SparseMat::Node* node = it.node(); float v2 = PH2->value(node->idx, (size_t*)&node->hashval); double a = v1 - v2; - double b = v1; + double b = (method == CV_COMP_CHISQR) ? v1 : v1 + v2; if( fabs(b) > DBL_EPSILON ) result += a*a/b; } @@ -2146,6 +2148,9 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method ) else CV_Error( CV_StsBadArg, "Unknown comparison method" ); + if( method == CV_COMP_CHISQR_ALT ) + result *= 2; + return result; } @@ -2485,13 +2490,13 @@ cvCompareHist( const CvHistogram* hist1, CvSparseMatIterator iterator; CvSparseNode *node1, *node2; - if( mat1->heap->active_count > mat2->heap->active_count && method != CV_COMP_CHISQR ) + if( mat1->heap->active_count > mat2->heap->active_count && method != CV_COMP_CHISQR && method != CV_COMP_CHISQR_ALT) { CvSparseMat* t; CV_SWAP( mat1, mat2, t ); } - if( method == CV_COMP_CHISQR ) + if( (method == CV_COMP_CHISQR) || (method == CV_COMP_CHISQR_ALT) ) { for( node1 = cvInitSparseMatIterator( mat1, &iterator ); node1 != 0; node1 = cvGetNextSparseNode( &iterator )) @@ -2500,7 +2505,7 @@ cvCompareHist( const CvHistogram* hist1, uchar* node2_data = cvPtrND( mat2, CV_NODE_IDX(mat1,node1), 0, 0, &node1->hashval ); double v2 = node2_data ? *(float*)node2_data : 0.f; double a = v1 - v2; - double b = v1; + double b = (method == CV_COMP_CHISQR) ? v1 : v1 + v2; if( fabs(b) > DBL_EPSILON ) result += a*a/b; } @@ -2590,6 +2595,9 @@ cvCompareHist( const CvHistogram* hist1, else CV_Error( CV_StsBadArg, "Unknown comparison method" ); + if( method == CV_COMP_CHISQR_ALT ) + result *= 2; + return result; } diff --git a/modules/imgproc/test/test_histograms.cpp b/modules/imgproc/test/test_histograms.cpp index ccdaa74f2..19ccc656b 100644 --- a/modules/imgproc/test/test_histograms.cpp +++ b/modules/imgproc/test/test_histograms.cpp @@ -948,7 +948,7 @@ int CV_ThreshHistTest::validate_test_results( int /*test_case_idx*/ ) class CV_CompareHistTest : public CV_BaseHistTest { public: - enum { MAX_METHOD = 4 }; + enum { MAX_METHOD = 5 }; CV_CompareHistTest(); protected: @@ -1014,6 +1014,8 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) result0[CV_COMP_INTERSECT] += MIN(v0,v1); if( fabs(v0) > DBL_EPSILON ) result0[CV_COMP_CHISQR] += (v0 - v1)*(v0 - v1)/v0; + if( fabs(v0 + v1) > DBL_EPSILON ) + result0[CV_COMP_CHISQR_ALT] += (v0 - v1)*(v0 - v1)/(v0 + v1); s0 += v0; s1 += v1; sq0 += v0*v0; @@ -1039,6 +1041,8 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) result0[CV_COMP_INTERSECT] += MIN(v0,v1); if( fabs(v0) > DBL_EPSILON ) result0[CV_COMP_CHISQR] += (v0 - v1)*(v0 - v1)/v0; + if( fabs(v0 + v1) > DBL_EPSILON ) + result0[CV_COMP_CHISQR_ALT] += (v0 - v1)*(v0 - v1)/(v0 + v1); s0 += v0; sq0 += v0*v0; result0[CV_COMP_BHATTACHARYYA] += sqrt(v0*v1); @@ -1053,6 +1057,8 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) } } + result0[CV_COMP_CHISQR_ALT] *= 2; + t = (sq0 - s0*s0/total_size)*(sq1 - s1*s1/total_size); result0[CV_COMP_CORREL] = fabs(t) > DBL_EPSILON ? (result0[CV_COMP_CORREL] - s0*s1/total_size)/sqrt(t) : 1; @@ -1067,6 +1073,7 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) double v = result[i], v0 = result0[i]; const char* method_name = i == CV_COMP_CHISQR ? "Chi-Square" : + i == CV_COMP_CHISQR_ALT ? "Alternative Chi-Square" : i == CV_COMP_CORREL ? "Correlation" : i == CV_COMP_INTERSECT ? "Intersection" : i == CV_COMP_BHATTACHARYYA ? "Bhattacharyya" : "Unknown";