OpenCV 2 学习笔记(21): 使用直方图比较检索相似图像

来源:互联网 发布:2015淘宝网销量排行榜 编辑:程序博客网 时间:2024/04/30 07:27

代码见:http://download.csdn.net/detail/u010525655/6303413

基于内容的图像检索在计算机视觉中是一个重要的方法。它从一组图片中找出一个和给定图片内容相似的图片。在前面我们已经学习过灰度直方图的相关知识,它包含了图像内容的许多信息。言下之意就是直方图可以被用来进行基于内容的图像检索。

也就是说我么可以通过简单的比较图像的直方图来测量两张图片的相似性。OpenCV有一个函数会估计它们之间的相似性。但是直方图必须是要经过定义的。这种比较方法在过去已经提出了很多,其中有些方法在cv::compareHist中实现了,让我们看一下函数的定义。

C++: double compareHist(InputArray H1, InputArray H2, int method)
C++: double compareHist(const SparseMat& H1, const SparseMat& H2, int method)


Parameters
H1 – First compared histogram.
H2 – Second compared histogram of the same size as H1 .
method – Comparison method that could be one of the following:
– CV_COMP_CORREL Correlation
– CV_COMP_CHISQR Chi-Square
– CV_COMP_INTERSECT Intersection
– CV_COMP_BHATTACHARYYA Bhattacharyya distance
– CV_COMP_HELLINGER Synonym for CV
_
COMP
_
BHATTACHARYYA
The functions compareHist compare two dense or two sparse histograms using the specified method:


由于OpenCV是开源的,所以我们可以获取他的源代码,让我们看看cv::compareHist是怎么实现的,我们 只看输入参数为InputArray的实现:

double cv::compareHist( InputArray _H1, InputArray _H2, int method ){    Mat H1 = _H1.getMat(), H2 = _H2.getMat();//H1如果本来是矩阵,那么他的类型不会变,维数也不便    const Mat* arrays[] = {&H1, &H2, 0};//指针数组,0的意思是不是和字符串'\0'的意思一样??    Mat planes[2];//保存每一个arrays元素    NAryMatIterator it(arrays, planes); //n元多维数组的迭代器    double result = 0;    int j, len = (int)it.size;    CV_Assert( H1.type() == H2.type() && H1.type() == CV_32F );//opencv断言,因为输入的是直方图,所以的类型要是CV_32F    double s1 = 0, s2 = 0, s11 = 0, s12 = 0, s22 = 0;    CV_Assert( it.planes[0].isContinuous() && it.planes[1].isContinuous() );    for( size_t i = 0; i < it.nplanes; i++, ++it )//nplanes = 1,因为只有一组Mat    {        const float* h1 = (const float*)it.planes[0].data;//指向图像数据的指针,it.planes[0]表示第一个Mat,以此类推,h1,h2分别指向H1和H2        const float* h2 = (const float*)it.planes[1].data;        len = it.planes[0].row s*it.planes[0].cols;//所有的像素数,不是所有元素个数,总的元素个数还应该乘以it.planes[0].channels,在这里他的值为1        if( method == CV_COMP_CHISQR )        {            for( j = 0; j < len; j++ )            {                double a = h1[j] - h2[j];                double b = h1[j];                if( fabs(b) > DBL_EPSILON )  //对角化的精确度 (典型地, DBL_EPSILON=≈10-15 就足够了)。                    result += a*a/b;            }        }        else if( method == CV_COMP_CORREL )        {            for( j = 0; j < len; j++ )            {                double a = h1[j];                double b = h2[j];                s12 += a*b;                s1 += a;                s11 += a*a;                s2 += b;                s22 += b*b;            }        }        else if( method == CV_COMP_INTERSECT )        {            for( j = 0; j < len; j++ )                result += std::min(h1[j], h2[j]);        }        else if( method == CV_COMP_BHATTACHARYYA )        {            for( j = 0; j < len; j++ )            {                double a = h1[j];                double b = h2[j];                result += std::sqrt(a*b);                s1 += a;                s2 += b;            }        }        else            CV_Error( CV_StsBadArg, "Unknown comparison method" );    }    if( method == CV_COMP_CORREL )    {        size_t total = H1.total();        double scale = 1./total;        double num = s12 - s1*s2*scale;        double denom2 = (s11 - s1*s1*scale)*(s22 - s2*s2*scale);        result = std::abs(denom2) > DBL_EPSILON ? num/std::sqrt(denom2) : 1.;    }    else if( method == CV_COMP_BHATTACHARYYA )    {        s1 *= s2;        s1 = fabs(s1) > FLT_EPSILON ? 1./std::sqrt(s1) : 1.;        result = std::sqrt(std::max(1. - result*s1, 0.));    }    return result;}


我们可以看到在opencv源代码中,将两个彩色图像直方图放进一个多维迭代器中,然后根据上面说明的方法名进行计算,代码就是实现上述算式的过程。注意上面的方法只适合于对于1,2,3维的稠密矩阵,再多维的矩阵就要用到另一个重载方法,这里我们没有给出来,因为上面的这个比较常用。注意InputArray参数,他只可以做参数,可以接收Mat, Mat_<T>, Matx<T, m, n>, std::vector<T>,std::vector<std::vector<T> > or std::vector<Mat>类型的参数。关于NaryMatIterator请看上一节。


下面我们写一个类ImageComparator。这个类从一组照片里面找出和给定照片内容最相似的照片。这个类包括一个待比较照片的引用,一个输入照片的引用,和他们的直方图。除此之外,因为我们需要使用彩色直方图进行比较,因此还需要一个ColorHistogram类,这个类我们前几节已经写好了。


另外为了较少计算量我们首先减少颜色的位深,这个我们在前面也已经说过了,由于这个方法给出的是比较最终的值, 所以就不给图示了。