简体   繁体   English

使用OpenCV通过Python和C ++计算基本矩阵的结果不同

[英]Different result in computing fundamental matrix by Python and C++ using OpenCV

I'm computing fundamental matrix for video odometry in Python and C++ using OpenCV. 我正在使用OpenCV计算Python和C ++中视频里程计的基本矩阵。 I've tried to keep the code in both implementations quite the same. 我试图保持两种实现中的代码完全相同。 However, I'm getting different results in both. 但是,我在两者中得到了不同的结果。 In Python, it works correctly, and in C++ it is showing completely incorrect results. 在Python中,它可以正常工作,而在C ++中它显示完全不正确的结果。 Below is a partial example of their code and outputs (first one in Python and second one in C++) 下面是他们的代码和输出的部分示例(第一个在Python中,第二个在C ++中)

Python version code: Python版本代码:

import os
import sys
import cv2
import numpy as np
import math

# Main Function
if __name__ == '__main__':
    K = np.matrix([[522.4825, 0,        300.9989], 
                   [0,        522.5723, 258.1389], 
                   [0.0,      0.0,      1.0]])
img1 = cv2.imread(sys.argv[1] + ".jpg")
img2 = cv2.imread(sys.argv[2] + ".jpg")

# sift = cv2.SURF()

detector = cv2.FeatureDetector_create("SURF")    # SURF, FAST, SIFT
descriptor = cv2.DescriptorExtractor_create("SURF") # SURF, SIFT

# kp1, des1 = sift.detectAndCompute(img1,None)
# kp2, des2 = sift.detectAndCompute(img2,None)

kp1 = detector.detect(img1)
kp2 = detector.detect(img2) 

k1, des1 = descriptor.compute(img1,kp1)
k2, des2 = descriptor.compute(img2,kp2)

# BFMatcher with default params
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1,des2, k=2)

good = []

# Apply ratio test
for m,n in matches:
    if m.distance < 0.7*n.distance:
            good.append(m)

MIN_MATCH_COUNT = 10
if len(good)>MIN_MATCH_COUNT:
    src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
    dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)
    F, mask = cv2.findFundamentalMat(src_pts, dst_pts, cv2.RANSAC, 5.0)
    matchesMask = mask.ravel().tolist()
else:
    print "Not enough matches are found - %d/%d" % (len(good),MIN_MATCH_COUNT)
    matchesMask = None

print F

And it's output: 它的输出:

[[ -3.22706105e-07   1.12585581e-04  -2.86938406e-02]
[ -1.16307090e-04  -5.04244159e-07   5.60714444e-02]
[  2.98839742e-02  -5.99974406e-02   1.00000000e+00]]

C++ version here: 这里的C ++版本:

#include <iostream>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/legacy/legacy.hpp>

using namespace std;

int main(int argc,char *argv[]) {
    //Define intrinsic matrix
    cv::Mat intrinsic = (cv::Mat_<double>(3,3) << 522.4825, 0, 300.9989,
            0, 522.5723, 258.1389,
            0, 0, 1);

    // Read input images
    string jpg1 = argv[1];
    jpg1.append(".jpg");
    string jpg2 = argv[2];
    jpg2.append(".jpg");
    cv::Mat image1 = cv::imread(jpg1,0);
    cv::Mat image2 = cv::imread(jpg2,0);
    if (!image1.data || !image2.data)
        return 0;

    // Display the images
    // cv::namedWindow("Image 1");
    // cv::imshow("Image 1",image1);
    // cv::namedWindow("Image 2");
    // cv::imshow("Image 2",image2);

    // pointer to the feature point detector object
    cv::Ptr<cv::FeatureDetector> detector = new cv::SurfFeatureDetector();
    // pointer to the feature descriptor extractor object
    cv::Ptr<cv::DescriptorExtractor> extractor = new cv::SurfDescriptorExtractor();

    // Detection of the SURF features
    vector<cv::KeyPoint> keypoints1, keypoints2;
    detector->detect(image1,keypoints1);
    detector->detect(image2,keypoints2);

    // Extraction of the SURF descriptors
    cv::Mat descriptors1, descriptors2;
    extractor->compute(image1,keypoints1,descriptors1);
    extractor->compute(image2,keypoints2,descriptors2);

    // Construction of the matcher
    cv::BruteForceMatcher<cv::L2<float> > matcher;

    vector<vector<cv::DMatch> > matches;
    vector<cv::DMatch> good_matches;
    matcher.knnMatch(descriptors1, descriptors2, matches, 2);

    for (vector<vector<cv::DMatch> >::iterator matchIterator= matches.begin();
         matchIterator!= matches.end(); ++matchIterator) {
        if ((*matchIterator)[0].distance < 0.7f * (*matchIterator)[1].distance) {
            good_matches.push_back((*matchIterator)[0]);
        }
    }

    // Convert keypoints into Point2f
    vector<cv::Point2f> src_pts, dst_pts;
    for (vector<cv::DMatch>::iterator it= good_matches.begin();
         it!= good_matches.end(); ++it)
    {
        // Get the position of left keypoints
        float x= keypoints1[it->queryIdx].pt.x;
        float y= keypoints1[it->queryIdx].pt.y;
        src_pts.push_back(cv::Point2f(x,y));
        // Get the position of right keypoints
        x= keypoints2[it->trainIdx].pt.x;
        y= keypoints2[it->trainIdx].pt.y;
        dst_pts.push_back(cv::Point2f(x,y));
    }
    // Compute F matrix using RANSAC
    cv::Mat fundemental = cv::findFundamentalMat(
            cv::Mat(src_pts),cv::Mat(dst_pts), // matching points
            CV_FM_RANSAC,  // RANSAC method
            5.0); // distance
    cout <<  fundemental << endl;

    return 0;
}

And its output: 它的输出:

[-4.310057787788129e-06, 0.0002459670522815174, -0.0413520716270485;
-0.0002531048911221476, -8.423657757958228e-08, 0.0974897887347238;
0.04566865455090797, -0.1062956485414729, 1]

Here are two test images: image 1 image 2 这是两个测试图像: 图像1 图像2

I can't find the reason. 我找不到原因。 Could anyone tell me why? 谁能告诉我为什么?

Since nobody is answering I'll share my thoughts. 既然没有人回答我会分享我的想法。 Do you check only numbers in F, or you apply it somehow and observe incorrect results? 你只检查F中的数字,或者你以某种方式应用它并观察不正确的结果? As @brandon-white has already noticed, floating-point precision may be one of the reasons. 正如@ brandon-white已经注意到的那样,浮点精度可能是其中一个原因。 But actually it is more complicated. 但实际上它更复杂。

First thing that comes to mind is that AFAIK in C++ OpenCV uses it's own routines for matrix and other math operations, while in python numpy is used where possible. 首先想到的是C ++ OpenCV中的AFAIK使用它自己的例程来进行矩阵和其他数学运算,而在python中尽可能使用numpy。 Maybe under the hood they use similar algorithms/implementations, but still you may get numerically different results, especially in cases where you deal with ambiguity (eigenvector decomposition, SVD, etc). 也许在引擎盖下他们使用类似的算法/实现,但是你仍然可以得到数字上不同的结果,特别是在处理模糊性(特征向量分解,SVD等)的情况下。

Also, you are using RANSAC to estimate F. In order to deal with (theoretically) any amount of outliers RANSAC takes a small random sample from all of your keypoints, and tries to find pairs, that satisfy some constraints. 此外,您正在使用RANSAC来估计F.为了处理(理论上)任何数量的异常值,RANSAC从所有关键点获取一个小的随机样本,并尝试找到满足某些约束的对。 It does this multiple times, and takes a best sample afterwards to calculate a final model. 它多次执行此操作,然后获取最佳样本以计算最终模型。 So basically you will end up with different points to estimate F each run, if you seed the pseudo-random generation routine appropriately. 因此,如果您适当地为伪随机生成例程播种,那么基本上您将最终得到不同的点来估计每次运行的F. But, usually homography and fundamental matrix estimators use a smarter approach, and after a sample that best satisfies the constraints is found - all points that satisfy this model are used to recalculate the matrix again. 但是,通常单应性和基本矩阵估计器使用更智能的方法,并且在找到最满足约束的样本之后 - 满足该模型的所有点用于再次重新计算矩阵。 This way you should get more consistent results, ideally same if RANSAC parameters are OK. 这样,您应该获得更一致的结果,理想情况下,如果RANSAC参数正常。 I am not sure whether it is used in OpenCV, but I guess it is. 我不确定它是否在OpenCV中使用,但我想是的。

Finally, there are degenerate cases , where F can't be fully estimated - case of planar motion, when all your keypoint lie on a plane (in 3D world), and purely rotational camera motion. 最后,有退化的情况 ,其中F无法完全估计 - 平面运动的情况,当你的所有关键点都在一个平面上(在3D世界中),以及纯粹的旋转相机运动。 Since you say that your code works in Python this is probably not the case, but still a point to consider. 既然你说你的代码在Python中工作,那可能不是这样,但仍然需要考虑。

So if you haven't done so yet - try to check F matrices you get on some data to ensure that the results you get are really different. 因此,如果您还没有这样做 - 尝试检查F矩阵,您获得一些数据,以确保您得到的结果真的不同。 In that case - there should be an error somewhere (admittedly I haven't closely checked your code yet). 在那种情况下 - 某处应该有错误(诚然,我还没有仔细检查过您的代码)。

Also, showing the matches you use for F computation may be useful for debugging, since that narrow the range of places, where your code may be behaving differently. 此外,显示用于F计算的匹配可能对调试很有用,因为这会缩小场所的范围,您的代码可能表现不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM