OpenCV - 冲浪算法 - 给出大量误报

时间:2014-04-08 11:23:44

标签: c++ opencv surf sift

我正在学习OpenCV,并开始探索用于图像匹配的SURF算法。我通过修改Microsoft Windows 7可用的默认映像创建了一个示例图像库。

每张图片在同一文件夹中都有旋转,缩放,模糊和倾斜的版本。

我找到匹配图像的代码如下所示。从代码中可以看出,距离是通过 dis / objectDescriptors-> total 来测量的,并且进一步的相似度是通过 100来计算的 - (dis / objectDescriptors-> total)* 100

不幸的是,这给了我一些奇怪的误报。例如,它将image1与完全不同的image2(85%相似度)匹配,但与image1的轻微模糊版本仅显示60%的相似度。

如何摆脱误报?

以下代码的灵感来自网站:http://opencvuser.blogspot.in/2012/07/surf-source-code-part-2.html

#include <cv.h>
#include <highgui.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h> 
#include <iostream>
#include <vector>

using namespace std;

static double dis=0;//For calculating the distance


IplImage *image = 0;

double
compareSURFDescriptors( const float* d1, const float* d2, double best, int length )
{
    double total_cost = 0;
    assert( length % 4 == 0 );
    for( int i = 0; i < length; i += 4 )
    {
        double t0 = d1[i] - d2[i];
        double t1 = d1[i+1] - d2[i+1];
        double t2 = d1[i+2] - d2[i+2];
        double t3 = d1[i+3] - d2[i+3];
        total_cost += t0*t0 + t1*t1 + t2*t2 + t3*t3;

      if( total_cost > best )
            break;
    }
    return total_cost;
}


int
naiveNearestNeighbor( const float* vec, int laplacian,
                      const CvSeq* model_keypoints,
                      const CvSeq* model_descriptors )
{
    int length = (int)(model_descriptors->elem_size/sizeof(float));
    int i, neighbor = -1;
    double d, dist1 = 1e6, dist2 = 1e6;
    CvSeqReader reader, kreader;
    cvStartReadSeq( model_keypoints, &kreader, 0 );
    cvStartReadSeq( model_descriptors, &reader, 0 );

    for( i = 0; i < model_descriptors->total; i++ )
    {
        const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
        const float* mvec = (const float*)reader.ptr;
     CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
        CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
        if( laplacian != kp->laplacian )
            continue;
        d = compareSURFDescriptors( vec, mvec, dist2, length );

        if( d < dist1 )
        {
            dist2 = dist1;
            dist1 = d;
            neighbor = i;
        }
        else if ( d < dist2 )
            dist2 = d;
    }
dis=dis+dist1;

/*We are finding the distance from every descriptor of probe image to every descriptor of the galley image. Finally in the findpairs function, we divide this distance with the total no. of descriptors to get the average of all the distances
*/
    if ( dist1 < 0.6*dist2 )
        return neighbor;
    return -1;
}

void
findPairs( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,
           const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, vector<int>& ptpairs )
{
    int i;
    CvSeqReader reader, kreader;
    cvStartReadSeq( objectKeypoints, &kreader );
    cvStartReadSeq( objectDescriptors, &reader );
    ptpairs.clear();

    for( i = 0; i < objectDescriptors->total; i++ )
    {
        const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
        const float* descriptor = (const float*)reader.ptr;
        CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
        CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
        int nearest_neighbor = naiveNearestNeighbor( descriptor, kp->laplacian, imageKeypoints, imageDescriptors);
//For every descriptor, we are trying to find it's nearest neighbour in the probe image
        if( nearest_neighbor >= 0 )
        {
            ptpairs.push_back(i);
            ptpairs.push_back(nearest_neighbor);
        }
    }

printf("\n%lf\n",(dis/objectDescriptors->total));////Here's where I am outputting the distance between the images
/*Dileep: If you are using this for recognition, write this distance to a file along with the name of the image you are matching against. After doing this for several images, you can then sort them in ascending order to find the best possible match - the one with the smallest distance. Here, I am outputting the distance to stdout
*/
}

int main(int argc, char** argv)
{
    const char* object_filename = argc == 3 ? argv[1] : "box.png";
    const char* scene_filename = argc == 3 ? argv[2] : "box_in_scene.png";
//Dileep:When you are excuting the object file, please write Command:./objectfile probe_image Gallery_image
/*Dileep:
Probe_image - This is the image for which you need to find the match
Gallery_image - This is one of the set of images, you use for matching

You keep the same probe image same, repeatedly changing the gallery image and outputting the distance in the format
<Gallery_name distance> into a file
Finally you can sort the distances in ascending order. And the one with the shortest distance - You can output it's name as the best possible match

It may become tedious to continually write the same command multiple times, changing the gallery file name. Try to use shell script with a for loop
*/
    CvMemStorage* storage = cvCreateMemStorage(0);





    IplImage* object = cvLoadImage( object_filename, CV_LOAD_IMAGE_GRAYSCALE );
    IplImage* image = cvLoadImage( scene_filename, CV_LOAD_IMAGE_GRAYSCALE );
    if( !object || !image )
    {
        fprintf( stderr, "Can not load %s and/or %s\n"
            "Usage: find_obj [<object_filename> <scene_filename>]\n",
            object_filename, scene_filename );
        exit(-1);
    }

    CvSeq *objectKeypoints = 0, *objectDescriptors = 0;
    CvSeq *imageKeypoints = 0, *imageDescriptors = 0;
    int i;
    CvSURFParams params = cvSURFParams(500, 1);

    double tt = (double)cvGetTickCount();
    cvExtractSURF( object, 0, &objectKeypoints, &objectDescriptors, storage, params );
    printf("Object Descriptors: %d\n", objectDescriptors->total);
    cvExtractSURF( image, 0, &imageKeypoints, &imageDescriptors, storage, params );
    printf("Image Descriptors: %d\n", imageDescriptors->total);
    tt = (double)cvGetTickCount() - tt;
    printf( "Extraction time = %gms\n", tt/(cvGetTickFrequency()*1000.));




    vector<int> ptpairs;

    findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );


    return 0;
}

3 个答案:

答案 0 :(得分:6)

请记住,通常的管道就是:

  1. 从两张图片中获取功能。
  2. 计算与邻居比率的假定对应关系(如您所做)或交叉匹配(@ user2746401)。
  3. 找到支持图像之间某种几何关系的一致对应的子集。
  4. 您已完成第1步和第2步,但缺少3步。请注意,推定的对应关系非常嘈杂。你将获得许多通信,其中许多错误,但有些正确。通过绘制它们很难确定它们是否正确计算。所以,你看到看似奇怪的对应关系是正常的。

    要执行第3步,在您的情况下,您可以找到与RANSAC的单应性,如@ user3481173所述。这很容易,因为OpenCV已经提供了同时执行这两项操作的函数cvFindHomography。在你的情况下,单应性应该很好,因为你正在处理平面图像的透视变换。

    顺便说一句,将来使用OpenCV的C ++ API会更容易。相同的代码可能会占用你的一半。

答案 1 :(得分:1)

我认为只需简单的配对匹配就可以轻松匹配两个图像。这样做的代价是巨大的。这就是您可能想要考虑使用RANSAC的原因。

我可以通过Zisserman and Kovesi

在Matlab中推荐一个例子

您需要Peter Kovesi

为RANSAC提供此功能

答案 2 :(得分:1)

我不认为有&#34;&#34;&#34;回答你的问题,抱歉。我可以就如何减少误报给出一些建议:

  • 交叉检查匹配。在此方案中,您可以找到图像B中图像A的特征的最接近匹配,然后匹配图像B中的特征,并在图像A中找回最近的邻居。如果这些匹配两种方式,那么您认为它是匹配的。 / p>

  • 比例匹配。在这里,您可以跟踪特征描述符之间的最近距离和第二个最近距离。如果最近与第二近似的比率满足某个阈值(例如,0.8),则将其保持为良好匹配。

他们应该让你的功能匹配只匹配&#34;好&#34;图像之间的功能。获得这些良好的特征匹配后,您可以看到哪些图像具有最佳的特征之间的平均距离。您可以使用阈值(正如您现在所做的那样),或者再次进行比率测试,以确保您选择的图像比另一个图像足够好。

如果您希望功能匹配进行图像检索,那么请搜索有关此主题的论文....这是一个非常开放的问题,所以请继续尝试!