为什么 cv::warpPerspective 在 Python 和 C++ 上对相同输入输出不同的值?

问题描述 投票:0回答:1

背景:

我正在尝试使用 ORB + Ransac 使用另一张图像作为参考来对齐图像。我使用 python 进行原型设计,并使用 c++ 通过 dart:ffi 部署到 android。然而,结果在 python 中很好,但在 c++ 中却不那么好,尽管如此,我认为我写了等效的代码。


配置:

C++

  • Opencv 4.8.0(从opencv.org下载并使用mingw编译)
  • 编译器:mingw
  • CMAKE

Python

  • 11.10.3
  • opencv-python == 4.8.0.76(我发现的最接近4.8.0的版本);

输入

两个输入图像是这里

输出

我期望这两个代码有相同的输出,但事实并非如此。请注意 cpp 图像右下角的黑色边框。

cpp_warped, py_warped


代码

C++

#include <opencv2/opencv.hpp>

using std::string;
using cv::Mat;

Mat warp(Mat source, Mat reference){
    
    cv::Ptr<cv::ORB> detector = cv::ORB::create(5000);

    cv::Mat d1, d2;
    std::vector<cv::KeyPoint> kp1, kp2;

    detector->detectAndCompute(source, cv::Mat(), kp1, d1);
    detector->detectAndCompute(reference, cv::Mat(), kp2, d2);

    cv::Ptr<cv::BFMatcher> matcher = cv::BFMatcher::create(cv::NORM_HAMMING, true);
    std::vector<cv::DMatch> matches;
    matcher->match(d1, d2, matches);

    // sort by distances (ascending)
    std::sort(matches.begin(), matches.end(), [] (cv::DMatch a, cv::DMatch b){
        return a.distance < b.distance;
    });

    // take only top 90% matches
    int n_matches = floor(matches.size() * 0.9);

    std::vector<cv::Point2f> p1, p2;

    for(int i = 0; i < n_matches; i++){
        p1.push_back(kp1[matches[i].queryIdx].pt);
        p2.push_back(kp2[matches[i].trainIdx].pt);
    }

    cv::Mat homography = cv::findHomography(p1, p2, cv::RANSAC);

    cv::Mat warped_mat;
    cv::warpPerspective(source, warped_mat, homography, cv::Size(reference.cols, reference.rows));

    cv::convertScaleAbs(warped_mat, warped_mat);

    return warped_mat;
}

int main(int argc, char const *argv[])
{
    Mat source = cv::imread("../source.jpg", cv::IMREAD_GRAYSCALE);
    Mat reference = cv::imread("../reference.png", cv::IMREAD_GRAYSCALE);

    // cv::imshow("source", source);
    // cv::waitKey(0);
    // cv::destroyAllWindows();

    Mat warped = warp(source, reference);


    cv::imwrite("cpp_warped.png", warped);
    return 0;
}

Python

import cv2 as cv
import numpy as np

def warp(source, reference):
    detector = cv.ORB.create(nfeatures=5000)

    kp1, d1 = detector.detectAndCompute(source, None)
    kp2, d2 = detector.detectAndCompute(reference, None)

    matcher = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True)
    matches = matcher.match(d1, d2)
    matches = sorted(matches, key= lambda x: x.distance)    

    #take top 90%
    matches = matches[:int(len(matches) * 0.9)]
    n_matches = len(matches)

    p1 = np.zeros((n_matches, 2))
    p2 = np.zeros((n_matches, 2))

    for i in range(n_matches):
        p1[i, :] = kp1[matches[i].queryIdx].pt
        p2[i, :] = kp2[matches[i].trainIdx].pt

    homography, _ = cv.findHomography(p1, p2, cv.RANSAC)

    height, width = reference.shape
    warped = cv.warpPerspective(source, homography, (width, height))
    warped = cv.convertScaleAbs(warped)

    return warped

if __name__ == "__main__":
    source = cv.imread("source.jpg", cv.IMREAD_GRAYSCALE)
    reference = cv.imread("reference.png", cv.IMREAD_GRAYSCALE)
    warped = warp(source, reference)
    cv.imwrite("py_warped.png", warped)

我仔细检查了 python 绑定和 opencv c++ 中的默认 cv::ORB 构造函数参数,它们是相同的。 我还尝试更改 c++ 代码上的一些参数,但没有达到与 python 一样好的结果。

opencv image-processing computer-vision
1个回答
0
投票

您检查过 findHomography() 的输出吗?

它使用 RANSAC,它不能保证每次运行都有相同的输出(至少如果你不能保证随机数生成具有相同的种子)。

如果您的数据有异常值,RANSAC 是必要的。 即使 LMEDS 也是基于随机采样,对异常值具有鲁棒性(LMEDS 仅当异常值小于 50% 时才能正常工作),因此不能保证每次运行都有相同的解决方案。

如果没有异常值,您可以使用使用所有输入点的标准最小二乘法(使用 0 而不是 RANSAC)。在这种情况下,每次运行的结果应该相同,因为最小二乘法遵循确定性的步骤序列。

© www.soinside.com 2019 - 2024. All rights reserved.