导入OpenCV的
calib3d, highgui, imgproc
模块以及C++的vector, iostream, fstream
库。定义了
getError2EpipLines
函数,这个函数用来计算两组点相对于F矩阵(基础矩阵)的投影误差。定义了
sgn
函数,用于返回一个双精度浮点数的符号。定义了
getPlanes
函数,用于通过RANSAC算法和3D点集从一系列点中找到若干平面,同时这些点会被分类到对应的平面。main
函数则完成了整个立体视觉的重建流程,具体步骤包括:
读取数据文件和图像;
从图像中检测关键点并提取SIFT特征描述符;
使用FLANN库进行高效特征匹配,并使用Lowe's比率测试筛选优秀的匹配;
通过匹配的特征点计算本质矩阵(Essential Matrix);
计算特征点到极线(epipolar lines)的平均误差;
本质矩阵分解得到旋转矩阵和平移矩阵;
通过三角化方法恢复出3D空间中的点;
对恢复出的3D点进行平面拟合,以识别出可能的物理平面;
显示和保存最终处理后的图像。
总的来说,这段代码展示了如何使用OpenCV库在计算机视觉中从两幅图像中恢复3D场景的几何信息。
leuvenA
leuvenB
essential_mat_data.txt
终端输出:
RANSAC essential matrix time 88023mcs.
Number of inliers 212
Mean error to epipolar lines 0.219943
Number of object points 212
#include "opencv2/calib3d.hpp" // 包含OpenCV相机校准和3D重建的库
#include "opencv2/highgui.hpp" // 包含OpenCV图像显示的库
#include "opencv2/imgproc.hpp" // 包含OpenCV图像处理的库
#include <vector> // 包含向量容器类的头文件
#include <iostream> // 包含标准输入输出流的头文件
#include <fstream> // 包含文件输入输出流的头文件
using namespace cv; // 使用OpenCV命名空间
static double getError2EpipLines(const Mat &F, const Mat &pts1, const Mat &pts2, const Mat &mask) {
Mat points1, points2;
// 将点坐标扩展为齐次坐标形式(在原坐标基础上增加一维,设为1)
vconcat(pts1, Mat::ones(1, pts1.cols, pts1.type()), points1);
vconcat(pts2, Mat::ones(1, pts2.cols, pts2.type()), points2);
double mean_error = 0;
// 计算并累积所有点到极线的距离作为误差
for (int pt = 0; pt < (int)mask.total(); pt++)
if (mask.at<uchar>(pt)) { // 如果该点是内点,则计算
const Mat l2 = F * points1.col(pt); // 使用基础矩阵F计算极线
const Mat l1 = F.t() * points2.col(pt); // 使用F的转置计算极线
// 计算点到极线的距离并取平均
mean_error += (fabs(points1.col(pt).dot(l1)) / sqrt(pow(l1.at<double>(0), 2) + pow(l1.at<double>(1), 2)) +
fabs(points2.col(pt).dot(l2)) / sqrt(pow(l2.at<double>(0), 2) + pow(l2.at<double>(1), 2))) / 2;
}
return mean_error / mask.total(); // 返回平均误差
}
static int sgn(double val) { return (0 < val) - (val < 0); } // 符号函数
/*
* @points3d - vector of Point3 or Mat of size Nx3
* @planes - vector of found planes
* @labels - vector of size point3d. Every point which has non-zero label is classified to this plane.
*/
// getPlanes函数,从3D点云中拟合多个平面
// @points3d - 3D点的集合,可以是Point3的向量或者是Nx3的Mat类型
// @planes - 存储找到的平面
// @labels - 和points3d大小相同的向量,每个非0标签的点都被归类到对应的平面
static void getPlanes (InputArray points3d_, std::vector<int> &labels, std::vector<Vec4d> &planes,
int desired_num_planes, double thr_, double conf_, int max_iters_) {
// 获取输入点并转换为双精度浮点数类型
Mat points3d = points3d_.getMat();
points3d.convertTo(points3d, CV_64F); // 将点转换为双精度
// 如果输入是一个向量,将其转换为Mat
if (points3d_.isVector())
points3d = Mat((int)points3d.total(), 3, CV_64F, points3d.data);
else {
// 如果输入的是单通道的Mat,转换为有1个通道
if (points3d.type() != CV_64F)
points3d = points3d.reshape(1, (int)points3d.total());
// 如果点的行