我正在关注this tutorial ,它使用 Features2D + Homography。如果我知道每张图像的相机矩阵,如何优化结果?我尝试了一些图像,但效果不佳。
//编辑
看了一些资料后,我想我应该先纠正两个图像。但校正并不完美,因此图像 1 上的垂直线通常对应于图像 2 上的垂直带。有什么好的算法吗?
最佳答案
我不确定我是否理解您的问题。您想找到图像之间的对应点,或者想通过使用相机内在函数来提高匹配的正确性?
原则上,为了使用相机几何形状来查找匹配,您将需要基本或基本矩阵,具体取决于您是否了解相机内在特性(即校准相机)。这意味着,您需要估计相机的相对旋转和平移。然后,通过计算与一张图像中找到的特征相对应的极线,您需要沿着第二张图像中的这些线进行搜索以找到最佳匹配。不过,我认为简单地依靠自动特征匹配会更好。给定基本/本质矩阵,您可以尝试一下 correctMatches
。 ,这将移动对应关系,以使重投影误差最小化。
更好匹配的提示
为了提高自动匹配的稳定性和显着性,通常需要这样做
- 调整特征检测器的参数
- 尝试不同的检测算法
执行比率测试以过滤掉那些具有非常相似的第二最佳匹配且因此不稳定的关键点。这样做是这样的:
Mat descriptors_1, descriptors_2; // obtained from feature detector BFMatcher matcher; vector<DMatch> matches; matcher = BFMatcher(NORM_L2, false); // norm depends on feature detector vector<vector<DMatch>> match_candidates; const float ratio = 0.8; // or something matcher.knnMatch(descriptors_1, descriptors_2, match_candidates, 2); for (int i = 0; i < match_candidates.size(); i++) { if (match_candidates[i][0].distance < ratio * match_candidates[i][1].distance) matches.push_back(match_candidates[i][0]); }
更复杂的过滤方法是计算第一帧中每个关键点的重投影误差。这意味着计算第二个图像中相应的极线,然后检查其假定的匹配点距该线有多远。丢弃距离超过某个阈值的那些点将删除与外极几何形状不兼容的匹配(我认为这是已知的)。计算错误可以这样完成(老实说,我不记得从哪里获取此代码,我可能对其进行了一些修改,当代码位于列表内时,SO 编辑器也有错误,对于格式错误表示抱歉):
关键是,获取基本矩阵,用它来计算所有点的线,然后计算距离(线以参数形式给出,因此您需要做一些代数来获得距离)。double computeReprojectionError(vector& imgpts1, vector& imgpts2, Mat& inlier_mask, const Mat& F) { double err = 0; vector lines[2]; int npt = sum(inlier_mask)[0]; <p></p> <p>// strip outliers so validation is constrained to the correspondences // which were used to estimate F vector imgpts1_copy(npt), imgpts2_copy(npt); int c = 0; for (int k = 0; k < inlier_mask.size().height; k++) { if (inlier_mask.at(0,k) == 1) { imgpts1_copy[c] = imgpts1[k]; imgpts2_copy[c] = imgpts2[k]; c++; } }</p> <p>Mat imgpt[2] = { Mat(imgpts1_copy), Mat(imgpts2_copy) }; computeCorrespondEpilines(imgpt[0], 1, F, lines[0]); computeCorrespondEpilines(imgpt<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>, 2, F, lines<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>); for(int j = 0; j < npt; j++ ) { // error is computed as the distance between a point u_l = (x,y) and the epipolar line of its corresponding point u_r in the second image plus the reverse, so errij = d(u_l, F^T * u_r) + d(u_r, F*u_l) Point2f u_l = imgpts1_copy[j], // for the purpose of this function, we imagine imgpts1 to be the "left" image and imgpts2 the "right" one. Doesn't make a difference u_r = imgpts2_copy[j]; float a2 = lines<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>[j][0], // epipolar line b2 = lines<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>[j]<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>, c2 = lines<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>[j][2]; float norm_factor2 = sqrt(pow(a2, 2) + pow(b2, 2)); float a1 = lines[0][j][0], b1 = lines[0][j]<a href="http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#correctmatches" rel="noreferrer noopener nofollow">1</a>, c1 = lines[0][j][2]; float norm_factor1 = sqrt(pow(a1, 2) + pow(b1, 2));</p> <p>double errij = fabs(u_l.x * a2 + u_l.y * b2 + c2) / norm_factor2 + fabs(u_r.x * a1 + u_r.y * b1 + c1) / norm_factor1; // distance of (x,y) to line (a,b,c) = ax + by + c / (a^2 + b^2) err += errij; // at this point, apply threshold and mark bad matches }</p> <p>return err / npt; } </p>
这与使用 RANSAC 方法的findFundamentalMat
的结果有些相似。它返回一个掩码,其中对于每个匹配,都有一个1
,这意味着它用于估计矩阵,或者如果它被丢弃,则有一个0
。但像这样估计基本矩阵可能不如使用棋盘准确。
关于c++ - 给定相机矩阵,如何使用 OpenCV 找到点对应关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36055581/