首页 文章

使用FLANN匹配从OpenCV SIFT列表中识别图像

提问于
浏览
6

应用程序的要点是从已经设置的图像列表中识别图像 . 图像列表已将其SIFT描述符提取并保存在文件中 . 这里没什么有趣的:

std::vector<cv::KeyPoint> detectedKeypoints;
cv::Mat objectDescriptors;

// Extract data
cv::SIFT sift;
sift.detect(image, detectedKeypoints);
sift.compute(image, detectedKeypoints, objectDescriptors);

// Save the file
cv::FileStorage fs(file, cv::FileStorage::WRITE);
fs << "descriptors" << objectDescriptors;
fs << "keypoints" << detectedKeypoints;
fs.release();

然后设备拍照 . SIFT描述符以相同的方式提取 . 现在的想法是将描述符与文件中的描述符进行比较 . 我是使用OpenCV的FLANN匹配器做的 . 我试图通过图像来量化相似性 . 经过整个清单后,我应该有最好的匹配 .

const cv::Ptr<cv::flann::IndexParams>& indexParams = new cv::flann::KDTreeIndexParams(1);
const cv::Ptr<cv::flann::SearchParams>& searchParams = new cv::flann::SearchParams(64);

// Match using Flann
cv::Mat indexMat;
cv::FlannBasedMatcher matcher(indexParams, searchParams);
std::vector< cv::DMatch > matches;
matcher.match(objectDescriptors, readDescriptors, matches);

匹配后我明白我得到了特征向量之间最近找到的距离列表 . 我找到了最小距离,并且使用它我可以计算“好的匹配”,甚至可以获得相应点的列表:

// Count the number of mathes where the distance is less than 2 * min_dist
int goodCount = 0;
for (int i = 0; i < objectDescriptors.rows; i++)
{
    if (matches[i].distance <  2 * min_dist)
    {
        ++goodCount;
        // Save the points for the homography calculation
        obj.push_back(detectedKeypoints[matches[i].queryIdx].pt);
        scene.push_back(readKeypoints[matches[i].trainIdx].pt);
    }
}

我正在展示代码的简单部分,只是为了让这更容易理解,我知道其中一些不需要在这里 .

继续,我希望简单地计算这样的好比赛的数量就足够了,但事实证明,大多只是指向具有最多描述符的图像 . 在此之后我尝试的是计算单应性 . 目的是计算它,看它是否是一个有效的同性恋 . 希望是一个很好的匹配,只有一个很好的匹配,才会有一个很好的转换单应性 . 创建单应性是在obj和场景上使用cv :: findHomography完成的,它们是std :: vector <cv :: Point2f> . 我使用在线发现的一些代码检查了单应性的有效性:

bool niceHomography(cv::Mat H)
{
    std::cout << H << std::endl;

    const double det = H.at<double>(0, 0) * H.at<double>(1, 1) - H.at<double>(1, 0) * H.at<double>(0, 1);
    if (det < 0)
    {
        std::cout << "Homography: bad determinant" << std::endl;
        return false;
    }

    const double N1 = sqrt(H.at<double>(0, 0) * H.at<double>(0, 0) + H.at<double>(1, 0) * H.at<double>(1, 0));
    if (N1 > 4 || N1 < 0.1)
    {
        std::cout << "Homography: bad first column" << std::endl;
        return false;
    }

    const double N2 = sqrt(H.at<double>(0, 1) * H.at<double>(0, 1) + H.at<double>(1, 1) * H.at<double>(1, 1));
    if (N2 > 4 || N2 < 0.1)
    {
        std::cout << "Homography: bad second column" << std::endl;
        return false;
    }

    const double N3 = sqrt(H.at<double>(2, 0) * H.at<double>(2, 0) + H.at<double>(2, 1) * H.at<double>(2, 1));
    if (N3 > 0.002)
    {
        std::cout << "Homography: bad third row" << std::endl;
        return false;
    }

    return true;
}

我不明白这背后的数学因此,在测试时,我有时用一个简单的检查来替换这个函数,以确定单应性的行列式是否为正 . 问题是我一直在这里遇到问题 . 单应性要么全都不好,要么就不好(当我只检查行列式时) .

我想我应该实际使用单应性,并且对于许多点,只需使用它们在源图像中的位置计算它们在目标图像中的位置 . 然后我会比较这些平均距离,理想情况下,在正确图像的情况下,我会获得非常明显的较小平均距离 . 这不起作用 at all . 所有的距离都是巨大的 . 我以为我可能已经使用了单应性来计算正确的位置,但是相互切换obj和场景给出了类似的结果 .

我试过的其他东西是SURF描述符而不是SIFT,BFMatcher(蛮力)而不是FLANN,为每个图像获得n个最小距离而不是根据最小距离获得数字,或者根据全局最大距离获得距离 . 这些方法都没有给我带来明确的好结果,我现在感到困惑 .

我唯一的下一个策略是锐化图像,甚至使用一些局部阈值或一些用于分割的算法将它们转换为二进制图像 . 我正在寻找任何人可以在我的工作中看到的任何建议或错误 .

我不知道这是否相关,但我添加了一些我正在测试它的图像 . 很多时候,在测试图像中,大多数SIFT矢量来自帧(高对比度)而不是绘画 . 这就是为什么我认为锐化图像可能会起作用的原因,但我不想更深入,以防我以前做过的事情是错误的 .

图库是here, Headers 中有描述 . 图像具有相当高的分辨率,请查看以防它可能提供一些提示 .

2 回答

  • 0

    你正确的方式 .

    首先,使用第二个最接近的比率而不是"good match by 2*min_dist" https://stackoverflow.com/a/23019889/1983544 .

    第二,以其他方式使用单应性 . 当你找到单应性时,你不仅有H,矩阵,还有与之一致的对应数量 . 检查它是否是一个合理的数字,比如> = 15 . 如果少于,则不匹配对象 .

    第三,如果你有一个大的视点变化,SIFT或SURF无法匹配图像 . 尝试使用MODS(http://cmp.felk.cvut.cz/wbs/这里是Windows和Linux二进制文件,以及描述算法的纸张)或ASIFT(慢得多,匹配更糟,但是开源)http://www.ipol.im/pub/art/2011/my-asift/

    或者至少使用MSER或Hessian-Affine检测器代替SIFT(保留SIFT作为描述符) .

  • 1

    您可以尝试测试匹配时,源图像和目标图像之间的线是否相对平行 . 如果它不是正确的匹配,那么你会有很多噪音并且线条不会平行 .

    请参阅显示正确匹配的附图(使用SURF和BF) - 所有行大多是平行的(尽管我应该指出这是一个简单的例子) .

    enter image description here

相关问题