OpenCV findFundamentalMat非常不稳定和敏感

时间:2014-08-11 20:16:58

标签: c++ opencv

我正在为我的大学工作,我们希望Quadrcopter能够用他的相机来稳定自己。不幸的是,基本矩阵对特征点内的微小变化反应非常敏感,我稍后会给你举例。

我认为由于ocv,我的匹配功能已经很好了。 我使用SURF功能并将其与knn-Method匹配:

    SurfFeatureDetector surf_detect;
    surf_detect = SurfFeatureDetector(400);

    //detect keypoints
    surf_detect.detect(fr_one.img, fr_one.kp);
    surf_detect.detect(fr_two.img, fr_two.kp);

    //extract keypoints
    SurfDescriptorExtractor surf_extract;
    surf_extract.compute(fr_one.img, fr_one.kp, fr_one.descriptors);
    surf_extract.compute(fr_two.img, fr_two.kp, fr_two.descriptors);

    //match keypoints
    vector<vector<DMatch> > matches1,matches2;
    vector<DMatch> symMatches,goodMatches;
    FlannBasedMatcher flann_match;

    flann_match.knnMatch(fr_one.descriptors, fr_two.descriptors, matches1,2);
    flann_match.knnMatch(fr_two.descriptors, fr_one.descriptors, matches2,2);

    //test matches in both ways
    symmetryTest(matches1,matches2,symMatches);

    std::vector<cv::Point2f> points1, points2;
    for (std::vector<cv::DMatch>::const_iterator it= symMatches.begin();
       it!= symMatches.end(); ++it)
    {
        //left keypoints
        float x= fr_one.kp[it->queryIdx].pt.x;
        float y= fr_one.kp[it->queryIdx].pt.y;
        points1.push_back(cv::Point2f(x,y));
        //right keypoints
        x = fr_two.kp[it->trainIdx].pt.x;
        y = fr_two.kp[it->trainIdx].pt.y;
        points2.push_back(cv::Point2f(x,y));
    }

    //kill outliers with ransac
    vector<uchar> inliers(points1.size(),0);
    findFundamentalMat(Mat(points1),Mat(points2),
                inliers,CV_FM_RANSAC,3.f,0.99f);

    std::vector<uchar>::const_iterator
    itIn= inliers.begin();
    std::vector<cv::DMatch>::const_iterator
    itM= symMatches.begin();
    for ( ;itIn!= inliers.end(); ++itIn, ++itM)
    {
        if (*itIn)
        {
            goodMatches.push_back(*itM);
        }
    }

现在我想用这些匹配来计算基本矩阵。我在这个例子中使用了8POINT方法 - 我已经用LMEDS和RANSAC尝试了它 - 它只会变得更糟,因为有更多的匹配会发生变化。

    vector<int> pointIndexes1;
    vector<int> pointIndexes2;
    for (vector<DMatch>::const_iterator it= goodMatches.begin();
         it!= goodMatches.end(); ++it) {
             pointIndexes1.push_back(it->queryIdx);
             pointIndexes2.push_back(it->trainIdx);
    }
    vector<Point2f> selPoints1, selPoints2;
    KeyPoint::convert(fr_one.kp,selPoints1,pointIndexes1);
    KeyPoint::convert(fr_two.kp,selPoints2,pointIndexes2);

    Mat F = findFundamentalMat(Mat(selPoints1),Mat(selPoints2),CV_FM_8POINT);

当我在同一对图像的循环内调用这些计算时,F的结果变化非常大 - 无法从这些计算中提取运动。

我生成了一个示例,其中我过滤掉了一些匹配项,以便您可以看到我自己提到的效果。

http://abload.de/img/div_c_01ascel.png

http://abload.de/img/div_c_02zpflj.png

我的代码有问题,还是我必须考虑图像质量等其他原因?

提前感谢您的帮助! derfreak

3 个答案:

答案 0 :(得分:10)

总结其他人已经陈述过的内容并详细阐述,

  1. 目前在OpenCV中实现,8点算法没有异常拒绝。它是最小二乘算法,不能与RANSAC或LMEDS一起使用,因为这些标志override the 8-point flag。建议对输入点进行归一化,以改善线性方程中矩阵的条件数,如"In Defence of the 8-point Algorithm"中所述。但是,OpenCV实现automatically normalizes the input points,因此无需手动规范化它们。

  2. 使用RANSAC或LMEDS,5点和7点算法都有异常值拒绝。如果您使用的是RANSAC,则可能需要调整阈值以获得良好的结果。 OpenCV documentation表明RANSAC的默认阈值是1.0,在我看来有点大。我可能会建议使用大约0.1像素的东西。另一方面,如果您使用LMEDS,则无需担心阈值,因为LMEDS最小化中值误差而不是计算内点。如果使用正确的阈值并且两者具有相当的计算时间,则LMEDS和RANSAC都具有相似的准确度。

  3. 5-point algorithm比7点算法更健壮,因为它只有5个自由度(3个旋转,2个用于单位矢量平移)而不是7个(另外2个参数是对于相机原理要点)。此最小参数化允许旋转和平移为easily extracted from the matrix using SVD并避免planar structure degeneracy problem

  4. 但是,为了使用5点算法获得准确的结果,必须知道焦距。本文建议焦距应在10%以内,否则5点算法并不比其他未校准算法更好。如果您之前未进行过相机校准,请查看OpenCV camera calibration tutorial。此外,如果您正在使用ROS,那么有一个很好的camera calibration package

  5. 使用OpenCV findEssentialMat function时,我建议先将像素点传递给undistortPoints。这不仅可以消除镜头失真的影响,还可以将坐标转换为标准化的图像坐标。标准化图像坐标(不要与8点算法中的标准化混淆)是不依赖于任何相机固有参数的相机不可知坐标。它们代表了轴承矢量与现实世界中的点的角度。例如,(1,0)的归一化图像坐标将对应于相机在x方向上的光轴为45度,在y方向上为0度的轴承角。

  6. 在使用RANSAC获得良好假设后,可以通过使用迭代鲁棒非线性最小二乘来提高最佳估计。这是mentioned in the paper,在"Bundle Adjustment - A Modern Synthesis"中有更详细的描述。不幸的是,似乎5点算法的OpenCV implementation不使用任何迭代细化方法。

答案 1 :(得分:1)

即使您的算法正确,由于图像噪声,8点F矩阵计算也非常容易出错。您使用的较少的对应关系越好。您可以做的最好的是进行5点基本(E)矩阵计算,但这需要您预先校准相机并将SIFT / SURF之后检测到的像素图像点转换为标准化像素(公制像素位置)。然后从免费提供的Matlab实现或Bundler(Noah Snavely的c ++实现)中应用Nister的5点算法。根据我对SfM的经验,5点E矩阵比7点或8点F矩阵计算好得多/稳定。并且在5点之后做RANSAC以获得更可靠的估计。希望这会有所帮助。

答案 2 :(得分:1)

8点算法是计算基本矩阵的最简单方法,但是如果小心,你可以很好地执行它。获得良好结果的关键是在构造要求解的方程之前对输入数据进行适当的仔细归一化。许多算法都可以做到。

像素点坐标必须更改为相机坐标,我不知道你正在做这些。据我了解,你的

vector<int> pointIndexes1;以像素坐标表示。 如果要获得更稳定的结果,您必须知道内在的相机参数。您可以通过多种方法找到它们:tutorial openCV。然后你有两个标准化它的选项。您可以申请基本矩阵,

Mat E = K.t() * F * K;其中K是内在相机参数。[见维基]

然而,这种假设并不准确。如果已知摄像机校准矩阵K,则可以对点x应用逆以获得以摄像机标准化坐标表示的点。

pointNormalize1= K.inv()*pointIndexes1其中pointIndexes1(2),z等于1.

在8PA的情况下,点的简单转换得到改善,因此结果的稳定性得以改善。建议的归一化是每个图像的平移和缩放,使得参考点的质心位于坐标的原点,并且来自原点的点的RMS距离等于![sqrt {2}]。请注意,建议在非规范化之前强制执行奇点条件。

参考:如果:you are still interested

,请检查
相关问题