我正在尝试使用 ORB + Ransac 使用另一张图像作为参考来对齐图像。我使用 python 进行原型设计,并使用 c++ 通过 dart:ffi 部署到 android。然而,结果在 python 中很好,但在 c++ 中却不那么好,尽管如此,我认为我写了等效的代码。
C++
Python
两个输入图像是这里
我期望这两个代码有相同的输出,但事实并非如此。请注意 cpp 图像右下角的黑色边框。
#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;
}
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 一样好的结果。
您检查过 findHomography() 的输出吗?
它使用 RANSAC,它不能保证每次运行都有相同的输出(至少如果你不能保证随机数生成具有相同的种子)。
如果您的数据有异常值,RANSAC 是必要的。 即使 LMEDS 也是基于随机采样,对异常值具有鲁棒性(LMEDS 仅当异常值小于 50% 时才能正常工作),因此不能保证每次运行都有相同的解决方案。
如果没有异常值,您可以使用使用所有输入点的标准最小二乘法(使用 0 而不是 RANSAC)。在这种情况下,每次运行的结果应该相同,因为最小二乘法遵循确定性的步骤序列。