我正在为我的大学制作一个项目,我们希望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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在我想用这些匹配来计算基本矩阵.我在这个例子中使用了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);
Run Code Online (Sandbox Code Playgroud)
当我在同一对图像的循环内调用这些计算时,F的结果变化很大 - 没有办法从这样的计算中提取运动.
我生成了一个例子,我过滤掉了一些匹配项,这样你就可以看到我自己提到的效果了.
http://abload.de/img/div_c_01ascel.png
http://abload.de/img/div_c_02zpflj.png
我的代码有问题还是我必须考虑其他原因,如图像质量等等?
在此先感谢您的帮助 !derfreak
sup*_*ver 13
总结其他人已经说明并详细阐述的内容,
正如目前在OpenCV中实现的那样,8点算法没有异常拒绝.它是最小二乘算法,不能与RANSAC或LMEDS一起使用,因为这些标志会覆盖8点标志.建议对输入点进行归一化,以改善线性方程中矩阵的条件数,如"保护8点算法"中所述.但是,OpenCV实现会自动规范化输入点,因此无需手动规范化它们.
使用RANSAC或LMEDS,5点和7点算法都具有异常值拒绝.如果您使用的是RANSAC,则可能需要调整阈值以获得良好的结果.在OpenCV的文档显示,RANSAC的默认阈值是1.0,这在我看来是有点大.我可能会建议使用大约0.1像素的东西.另一方面,如果您使用LMEDS,则无需担心阈值,因为LMEDS最小化中值误差而不是计算内点.如果使用正确的阈值并且两者具有相当的计算时间,则LMEDS和RANSAC都具有相似的准确度.
在5点算法比7点算法更健壮,因为它只有而不是5个自由度(3旋转和2为单位向量翻译)7(附加2个参数是用于照相机原理点).该最小参数化允许使用SVD容易地从矩阵中提取旋转和平移,并避免平面结构简并问题.
但是,为了使用5点算法获得准确的结果,必须知道焦距.本文建议焦距应在10%以内,否则5点算法并不比其他未校准算法更好.如果您之前没有进行过相机校准,请查看OpenCV相机校准教程.此外,如果您使用ROS,还有一个很好的相机校准包.
当使用OpenCV findEssentialMat函数时,我建议首先将像素点传递给undistortPoints.这不仅可以消除镜头失真的影响,还可以将坐标转换为标准化的图像坐标.标准化图像坐标(不要与8点算法中的标准化混淆)是不依赖于任何相机固有参数的相机不可知坐标.它们代表了轴承矢量与现实世界中的点的角度.例如,(1,0)的归一化图像坐标将对应于从x方向上的相机的光轴和y方向上的0度的45度的方位角.
在使用RANSAC获得良好假设之后,可以通过使用迭代鲁棒非线性最小二乘来改善最佳估计.这在论文中提到并在"束调整 - 现代合成"中有更详细的描述.不幸的是,似乎5点算法的OpenCV实现不使用任何迭代细化方法.
小智 2
即使你的算法是正确的,由于图像噪声,8 点 F 矩阵计算也很容易出错。使用的对应关系越少越好。您能做的最好的事情就是进行 5 点基本 (E) 矩阵计算,但这需要您预先校准相机并将 SIFT/SURF 后检测到的像素图像点转换为标准化像素(公制像素位置)。然后从免费提供的 Matlab 实现或 Bundler(Noah Snavely 的 C++ 实现)应用 Nister 的 5 点算法。根据我使用 SfM 的经验,5 点 E 矩阵比 7 或 8 点 F 矩阵计算要好得多/稳定得多。当然,在 5 点后进行 RANSAC 以获得更稳健的估计。希望这可以帮助。