Commit 086db9d6 authored by kocheganovvm's avatar kocheganovvm

Introduce CV_COMP_CHISQR_ALT, an alternative method to calculate ChiSquare Histogram comparison

There's some disagreement about the correct formula.
has its supporters, however, for texture analysis, the newly introduced formula became
standard. The commit enables both uses without breaking backward compatibility.

First contributor of this commit was sperrholz.
parent 04c86f28
...@@ -173,6 +173,8 @@ Compares two histograms. ...@@ -173,6 +173,8 @@ Compares two histograms.
* **CV_COMP_CHISQR** Chi-Square * **CV_COMP_CHISQR** Chi-Square
* **CV_COMP_CHISQR_ALT** Alternative Chi-Square
* **CV_COMP_INTERSECT** Intersection * **CV_COMP_INTERSECT** Intersection
* **CV_COMP_BHATTACHARYYA** Bhattacharyya distance * **CV_COMP_BHATTACHARYYA** Bhattacharyya distance
...@@ -202,6 +204,14 @@ The functions ``compareHist`` compare two dense or two sparse histograms using t ...@@ -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)} 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``) * Intersection (``method=CV_COMP_INTERSECT``)
.. math:: .. math::
...@@ -493,3 +503,4 @@ The function clears histogram bins that are below the specified threshold. ...@@ -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. .. [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.
...@@ -508,7 +508,8 @@ enum ...@@ -508,7 +508,8 @@ enum
CV_COMP_CHISQR =1, CV_COMP_CHISQR =1,
CV_COMP_INTERSECT =2, CV_COMP_INTERSECT =2,
CV_COMP_BHATTACHARYYA =3, 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 */ /* Mask size for distance transform */
......
...@@ -1990,12 +1990,12 @@ double cv::compareHist( InputArray _H1, InputArray _H2, int method ) ...@@ -1990,12 +1990,12 @@ double cv::compareHist( InputArray _H1, InputArray _H2, int method )
const float* h2 = (const float*)it.planes[1].data; const float* h2 = (const float*)it.planes[1].data;
len = it.planes[0].rows*it.planes[0].cols*H1.channels(); 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++ ) for( j = 0; j < len; j++ )
{ {
double a = h1[j] - h2[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 ) if( fabs(b) > DBL_EPSILON )
result += a*a/b; result += a*a/b;
} }
...@@ -2034,7 +2034,9 @@ double cv::compareHist( InputArray _H1, InputArray _H2, int method ) ...@@ -2034,7 +2034,9 @@ double cv::compareHist( InputArray _H1, InputArray _H2, int method )
CV_Error( CV_StsBadArg, "Unknown comparison 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(); size_t total = H1.total();
double scale = 1./total; double scale = 1./total;
...@@ -2063,13 +2065,13 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method ) ...@@ -2063,13 +2065,13 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method )
CV_Assert( H1.size(i) == H2.size(i) ); CV_Assert( H1.size(i) == H2.size(i) );
const SparseMat *PH1 = &H1, *PH2 = &H2; 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); std::swap(PH1, PH2);
SparseMatConstIterator it = PH1->begin(); SparseMatConstIterator it = PH1->begin();
int N1 = (int)PH1->nzcount(), N2 = (int)PH2->nzcount(); 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 ) for( i = 0; i < N1; i++, ++it )
{ {
...@@ -2077,7 +2079,7 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method ) ...@@ -2077,7 +2079,7 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method )
const SparseMat::Node* node = it.node(); const SparseMat::Node* node = it.node();
float v2 = PH2->value<float>(node->idx, (size_t*)&node->hashval); float v2 = PH2->value<float>(node->idx, (size_t*)&node->hashval);
double a = v1 - v2; double a = v1 - v2;
double b = v1; double b = (method == CV_COMP_CHISQR) ? v1 : v1 + v2;
if( fabs(b) > DBL_EPSILON ) if( fabs(b) > DBL_EPSILON )
result += a*a/b; result += a*a/b;
} }
...@@ -2146,6 +2148,9 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method ) ...@@ -2146,6 +2148,9 @@ double cv::compareHist( const SparseMat& H1, const SparseMat& H2, int method )
else else
CV_Error( CV_StsBadArg, "Unknown comparison method" ); CV_Error( CV_StsBadArg, "Unknown comparison method" );
if( method == CV_COMP_CHISQR_ALT )
result *= 2;
return result; return result;
} }
...@@ -2485,13 +2490,13 @@ cvCompareHist( const CvHistogram* hist1, ...@@ -2485,13 +2490,13 @@ cvCompareHist( const CvHistogram* hist1,
CvSparseMatIterator iterator; CvSparseMatIterator iterator;
CvSparseNode *node1, *node2; 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; CvSparseMat* t;
CV_SWAP( mat1, mat2, 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 ); for( node1 = cvInitSparseMatIterator( mat1, &iterator );
node1 != 0; node1 = cvGetNextSparseNode( &iterator )) node1 != 0; node1 = cvGetNextSparseNode( &iterator ))
...@@ -2500,7 +2505,7 @@ cvCompareHist( const CvHistogram* hist1, ...@@ -2500,7 +2505,7 @@ cvCompareHist( const CvHistogram* hist1,
uchar* node2_data = cvPtrND( mat2, CV_NODE_IDX(mat1,node1), 0, 0, &node1->hashval ); uchar* node2_data = cvPtrND( mat2, CV_NODE_IDX(mat1,node1), 0, 0, &node1->hashval );
double v2 = node2_data ? *(float*)node2_data : 0.f; double v2 = node2_data ? *(float*)node2_data : 0.f;
double a = v1 - v2; double a = v1 - v2;
double b = v1; double b = (method == CV_COMP_CHISQR) ? v1 : v1 + v2;
if( fabs(b) > DBL_EPSILON ) if( fabs(b) > DBL_EPSILON )
result += a*a/b; result += a*a/b;
} }
...@@ -2590,6 +2595,9 @@ cvCompareHist( const CvHistogram* hist1, ...@@ -2590,6 +2595,9 @@ cvCompareHist( const CvHistogram* hist1,
else else
CV_Error( CV_StsBadArg, "Unknown comparison method" ); CV_Error( CV_StsBadArg, "Unknown comparison method" );
if( method == CV_COMP_CHISQR_ALT )
result *= 2;
return result; return result;
} }
......
...@@ -948,7 +948,7 @@ int CV_ThreshHistTest::validate_test_results( int /*test_case_idx*/ ) ...@@ -948,7 +948,7 @@ int CV_ThreshHistTest::validate_test_results( int /*test_case_idx*/ )
class CV_CompareHistTest : public CV_BaseHistTest class CV_CompareHistTest : public CV_BaseHistTest
{ {
public: public:
enum { MAX_METHOD = 4 }; enum { MAX_METHOD = 5 };
CV_CompareHistTest(); CV_CompareHistTest();
protected: protected:
...@@ -1014,6 +1014,8 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) ...@@ -1014,6 +1014,8 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ )
result0[CV_COMP_INTERSECT] += MIN(v0,v1); result0[CV_COMP_INTERSECT] += MIN(v0,v1);
if( fabs(v0) > DBL_EPSILON ) if( fabs(v0) > DBL_EPSILON )
result0[CV_COMP_CHISQR] += (v0 - v1)*(v0 - v1)/v0; 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; s0 += v0;
s1 += v1; s1 += v1;
sq0 += v0*v0; sq0 += v0*v0;
...@@ -1039,6 +1041,8 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) ...@@ -1039,6 +1041,8 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ )
result0[CV_COMP_INTERSECT] += MIN(v0,v1); result0[CV_COMP_INTERSECT] += MIN(v0,v1);
if( fabs(v0) > DBL_EPSILON ) if( fabs(v0) > DBL_EPSILON )
result0[CV_COMP_CHISQR] += (v0 - v1)*(v0 - v1)/v0; 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; s0 += v0;
sq0 += v0*v0; sq0 += v0*v0;
result0[CV_COMP_BHATTACHARYYA] += sqrt(v0*v1); result0[CV_COMP_BHATTACHARYYA] += sqrt(v0*v1);
...@@ -1053,6 +1057,8 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ ) ...@@ -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); t = (sq0 - s0*s0/total_size)*(sq1 - s1*s1/total_size);
result0[CV_COMP_CORREL] = fabs(t) > DBL_EPSILON ? result0[CV_COMP_CORREL] = fabs(t) > DBL_EPSILON ?
(result0[CV_COMP_CORREL] - s0*s1/total_size)/sqrt(t) : 1; (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*/ ) ...@@ -1067,6 +1073,7 @@ int CV_CompareHistTest::validate_test_results( int /*test_case_idx*/ )
double v = result[i], v0 = result0[i]; double v = result[i], v0 = result0[i];
const char* method_name = const char* method_name =
i == CV_COMP_CHISQR ? "Chi-Square" : i == CV_COMP_CHISQR ? "Chi-Square" :
i == CV_COMP_CHISQR_ALT ? "Alternative Chi-Square" :
i == CV_COMP_CORREL ? "Correlation" : i == CV_COMP_CORREL ? "Correlation" :
i == CV_COMP_INTERSECT ? "Intersection" : i == CV_COMP_INTERSECT ? "Intersection" :
i == CV_COMP_BHATTACHARYYA ? "Bhattacharyya" : "Unknown"; i == CV_COMP_BHATTACHARYYA ? "Bhattacharyya" : "Unknown";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment