三维重建sfm的openCV实现

#include <opencv2\features2d\features2d.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\calib3d\calib3d.hpp>
#include <iostream>

using namespace cv;
using namespace std;

void extract_features(
	vector<string>& image_names,
	vector<vector<KeyPoint>>& key_points_for_all,
	vector<Mat>& descriptor_for_all,
	vector<vector<Vec3b>>& colors_for_all
)
{
	key_points_for_all.clear();
	descriptor_for_all.clear();
	Mat image;

	//读取图像,获取图像特征点,并保存
	Ptr<FeatureDetector> sift = cv::SIFT::create();
	//Ptr<Feature2D> sift = xfeatures2d::SIFT::create(0, 3, 0.04, 10);
	for (auto it = image_names.begin(); it != image_names.end(); ++it)
	{
		image = imread(*it);
		if (image.empty()) continue;

		vector<KeyPoint> key_points;
		Mat descriptor;
		//偶尔出现内存分配失败的错误
		sift->detectAndCompute(image, noArray(), key_points, descriptor);

		//特征点过少,则排除该图像
		if (key_points.size() <= 10) continue;

		key_points_for_all.push_back(key_points);
		descriptor_for_all.push_back(descriptor);

		vector<Vec3b> colors(key_points.size());
		for (int i = 0; i < key_points.size(); ++i)
		{
			Point2f& p = key_points[i].pt;
			colors[i] = image.at<Vec3b>(p.y, p.x);
		}
		colors_for_all.push_back(colors);
	}
}

void match_features(Mat& query, Mat& train, vector<DMatch>& matches)
{
	vector<vector<DMatch>> knn_matches;
	BFMatcher matcher(NORM_L2);
	matcher.knnMatch(query, train, knn_matches, 2);

	//获取满足Ratio Test的最小匹配的距离
	float min_dist = FLT_MAX;
	for (int r = 0; r < knn_matches.size(); ++r)
	{
		//Ratio Test
		if (knn_matches[r][0].distance > 0.6 * knn_matches[r][1].distance)
			continue;

		float dist = knn_matches[r][0].distance;
		if (dist < min_dist) min_dist = dist;
	}

	matches.clear();
	for (size_t r = 0; r < knn_matches.size(); ++r)
	{
		//排除不满足Ratio Test的点和匹配距离过大的点
		if (
			knn_matches[r][0].distance > 0.6 * knn_matches[r][1].distance ||
			knn_matches[r][0].distance > 5 * max(min_dist, 10.0f)
			)
			continue;

		//保存匹配点
		matches.push_back(knn_matches[r][0]);
	}
}

bool find_transform(Mat& K, vector<Point2f>& p1, vector<Point2f>& p2, Mat& R, Mat& T, Mat& mask)
{
	//根据内参矩阵获取相机的焦距和光心坐标(主点坐标)
	double focal_length = 0.5 * (K.at<double>(0) + K.at<double>(4));
	Point2d principle_point(K.at<double>(2), K.at<double>(5));

	//根据匹配点求取本征矩阵,使用RANSAC,进一步排除失配点
	Mat E = findEssentialMat(p1, p2, focal_length, principle_point, RANSAC, 0.999, 1.0, mask);
	if (E.empty()) return false;

	double feasible_count = countNonZero(mask);
	cout << (int)feasible_count << " -in- " << p1.size() << endl;
	//对于RANSAC而言,outlier数量大于50%时,结果是不可靠的
	if (feasible_count <= 15 || (feasible_count / p1.size()) < 0.6)
		return false;

	//分解本征矩阵,获取相对变换
	int pass_count = recoverPose(E, p1, p2, R, T, focal_length, principle_point, mask);

	//同时位于两个相机前方的点的数量要足够大
	if (((double)pass_count) / feasible_count < 0.7)
		return false;

	return true;
}

void get_matched_points(
	vector<KeyPoint>& p1,
	vector<KeyPoint>& p2,
	vector<DMatch> matches,
	vector<Point2f>& out_p1,
	vector<Point2f>& out_p2
)
{
	out_p1.clear();
	out_p2.clear();
	for (int i = 0; i < matches.size(); ++i)
	{
		out_p1.push_back(p1[matches[i].queryIdx].pt);
		out_p2.push_back(p2[matches[i].trainIdx].pt);
	}
}

void get_matched_colors(
	vector<Vec3b>& c1,
	vector<Vec3b>& c2,
	vector<DMatch> matches,
	vector<Vec3b>& out_c1,
	vector<Vec3b>& out_c2
)
{
	out_c1.clear();
	out_c2.clear();
	for (int i = 0; i < matches.size(); ++i)
	{
		out_c1.push_back(c1[matches[i].queryIdx]);
		out_c2.push_back(c2[matches[i].trainIdx]);
	}
}

void reconstruct(Mat& K, Mat& R, Mat& T, vector<Point2f>& p1, vector<Point2f>& p2, Mat& structure)
{
	//两个相机的投影矩阵[R T],triangulatePoints只支持float型
	Mat proj1(3, 4, CV_32FC1);
	Mat proj2(3, 4, CV_32FC1);

	proj1(Range(0, 3), Range(0, 3)) = Mat::eye(3, 3, CV_32FC1);
	proj1.col(3) = Mat::zeros(3, 1, CV_32FC1);

	R.convertTo(proj2(Range(0, 3), Range(0, 3)), CV_32FC1);
	T.convertTo(proj2.col(3), CV_32FC1);

	Mat fK;
	K.convertTo(fK, CV_32FC1);
	proj1 = fK * proj1;
	proj2 = fK * proj2;

	//三角重建
	triangulatePoints(proj1, proj2, p1, p2, structure);
}

void maskout_points(vector<Point2f>& p1, Mat& mask)
{
	vector<Point2f> p1_copy = p1;
	p1.clear();

	for (int i = 0; i < mask.rows; ++i)
	{
		if (mask.at<uchar>(i) > 0)
			p1.push_back(p1_copy[i]);
	}
}

void maskout_colors(vector<Vec3b>& p1, Mat& mask)
{
	vector<Vec3b> p1_copy = p1;
	p1.clear();

	for (int i = 0; i < mask.rows; ++i)
	{
		if (mask.at<uchar>(i) > 0)
			p1.push_back(p1_copy[i]);
	}
}

void save_structure(string file_name, vector<Mat>& rotations, vector<Mat>& motions, Mat& structure, vector<Vec3b>& colors)
{
	int n = (int)rotations.size();

	FileStorage fs(file_name, FileStorage::WRITE);
	fs << "Camera Count" << n;
	fs << "Point Count" << structure.cols;

	fs << "Rotations" << "[";
	for (size_t i = 0; i < n; ++i)
	{
		fs << rotations[i];
	}
	fs << "]";

	fs << "Motions" << "[";
	for (size_t i = 0; i < n; ++i)
	{
		fs << motions[i];
	}
	fs << "]";

	fs << "Points" << "[";
	for (size_t i = 0; i < structure.cols; ++i)
	{
		Mat_<float> c = structure.col(i);
		c /= c(3);	//齐次坐标,需要除以最后一个元素才是真正的坐标值
		fs << Point3f(c(0), c(1), c(2));
	}
	fs << "]";

	fs << "Colors" << "[";
	for (size_t i = 0; i < colors.size(); ++i)
	{
		fs << colors[i];
	}
	fs << "]";

	fs.release();
}

void main()
{
	string img1 = "C:\\Users\\jiang\\Desktop\\tempfile\\0419\\1 (3).jpg";
	string img2 = "C:\\Users\\jiang\\Desktop\\tempfile\\0419\\1 (4).jpg";
	vector<string> img_names = { img1, img2 };

	vector<vector<KeyPoint>> key_points_for_all;
	vector<Mat> descriptor_for_all;
	vector<vector<Vec3b>> colors_for_all;
	vector<DMatch> matches;
	vector<Point2f> p1, p2;
	vector<Vec3b> c1, c2;
	Mat R, T;	//旋转矩阵和平移向量
	Mat mask;	//mask中大于零的点代表匹配点,等于零代表失配点
	//本征矩阵
	Mat K(Matx33d(
		2759.48, 0, 1520.69,
		0, 2764.16, 1006.81,
		0, 0, 1));

	//提取特征
	double time_ = static_cast<double>(getTickCount());
	double time0 = 0;
	extract_features(img_names, key_points_for_all, descriptor_for_all, colors_for_all);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "提取特征耗时=" <<time0<< endl;
	//特征匹配
	match_features(descriptor_for_all[0], descriptor_for_all[1], matches);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "特征匹配耗时=" << time0 << endl;
	//计算变换矩阵

	get_matched_points(key_points_for_all[0], key_points_for_all[1], matches, p1, p2);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "get_matched_points=" << time0 << endl;
	get_matched_colors(colors_for_all[0], colors_for_all[1], matches, c1, c2);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "get_matched_colors=" << time0 << endl;
	find_transform(K, p1, p2, R, T, mask);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "find_transform=" << time0 << endl;
	//三维重建
	Mat structure;	//4行N列的矩阵,每一列代表空间中的一个点(齐次坐标)
	maskout_points(p1, mask);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "p1  maskout_points=" << time0 << endl;
	maskout_points(p2, mask);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "p2  maskout_points=" << time0 << endl;
	reconstruct(K, R, T, p1, p2, structure);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "reconstruct=" << time0 << endl;
	//保存并显示
	vector<Mat> rotations = { Mat::eye(3, 3, CV_64FC1), R };
	vector<Mat> motions = { Mat::zeros(3, 1, CV_64FC1), T };
	maskout_colors(c1, mask);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "maskout_colors=" << time0 << endl;
	save_structure("C:\\Users\\jiang\\Desktop\\workpath\\stereo\\structure.yml", rotations, motions, structure, c1);
	time0 = ((double)getTickCount() - time_) / getTickFrequency();
	cout << "save_structure=" << time0 << endl;
	//system(".\\Viewer\\SfMViewer.exe");
}

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: SFMViewer是一款用于查看和编辑Source Filmmaker (SFM)工程文件的软件。SFM是一种由Valve Corporation开发的电影制作工具,可以用来制作动画和视频。SFMViewer具有直观的用户界面和丰富的功能,使用户可以轻松地浏览、预览和编辑SFM文件。 要下载SFMViewer,可以按照以下步骤进行操作: 1. 打开您的网络浏览器,例如Chrome、Firefox等。 2. 在搜索栏中输入"SFMViewer下载"或者前往SFMViewer官方网站。 3. 在搜索结果中选择一个可靠且安全的网站,最好是官方网站或受信任的软件下载网站。 4. 寻找SFMViewer的相关下载链接或按钮,并单击下载按钮。 5. 根据您的操作系统选择正确的版本,如Windows、Mac或Linux。 6. 确保您的设备具有足够的储存空间,并单击下载按钮开始下载SFMViewer的安装文件。 7. 下载完成后,找到安装文件并双击运行。 8. 按照安装向导的提示进行操作,一般而言,只需按照默认设置点击"下一步"直至安装完成。 9. 安装完成后,您可以在电脑的应用程序列表中找到SFMViewer的图标,并双击打开程序。 10. 使用SFMViewer打开您的SFM工程文件,您可以开始浏览、预览和编辑您的动画或视频了。 请注意,下载任何软件时应保持警惕,确保您从可信赖的来源下载软件,并在安装过程中注意勾选/取消勾选附加软件的安装选项,以避免安装不需要的工具条或其他不必要的软件。 ### 回答2: SFMViewer是一款专为3D动画爱好者和专业人士设计的开源软件,专门用于查看、编辑和渲染源引擎制作的3D动画文件。它支持的文件格式包括Valve公司开发的Source Filmmaker (SFM)格式以及一些其他常见的3D文件格式。 要下载SFMViewer,你可以按照以下步骤操作: 1. 打开你的互联网浏览器,比如谷歌浏览器、火狐浏览器等。 2. 在搜索栏中输入"SFMViewer下载",然后按下回车键或者点击搜索按钮。 3. 在搜索结果页面中,找到一个可信赖的网站提供SFMViewer的下载链接。 4. 点击下载链接,然后保存下载文件到你的电脑上,通常会保存在默认的下载文件夹中。 5. 打开下载文件夹,找到SFMViewer的安装文件,并双击运行它。 6. 按照安装向导的提示,选择安装目录和其他选项,然后点击"安装"按钮进行安装。 7. 等待安装完成后,你可以在开始菜单、桌面快捷方式或安装目录中找到SFMViewer的运行图标。 8. 双击运行图标,即可打开SFMViewer,开始使用它查看和编辑你的3D动画文件。 需要注意的是,在下载和安装SFMViewer之前,要确保你的电脑满足软件的硬件需求,并且要从可信赖的网站下载以避免安装恶意软件。此外,为了更好地使用SFMViewer,你可能需要阅读软件的使用说明或参考在线教程。 总之,通过以上步骤,你可以很方便地下载和安装SFMViewer,并使用它来查看和编辑你的3D动画文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值