OPENCV2计算机视觉编程手册-第九章估算图像间的投影关系

1、计算一对图像的基础矩阵

#include<iostream>  
#include<opencv2/core/core.hpp>    
#include<opencv2/highgui/highgui.hpp> 
#include<opencv2/features2d/features2d.hpp>
#include "opencv2/nonfree/nonfree.hpp"  
#include<opencv2/legacy/legacy.hpp>  
#include<imgproc/imgproc.hpp>  
 
using namespace std;
using namespace cv;
 
int main(){
 
	Mat image1 = cvLoadImage("C:\\Users\\Administrator\\Desktop\\7.jpg");
	Mat image2 = cvLoadImage("C:\\Users\\Administrator\\Desktop\\8.jpg");
	if (!image1.data || !image2.data)
		return 0;
 
	//特征点的向量
	vector<KeyPoint> keypoints1, keypoints2;
	//构造SURF特征检测器
	SurfFeatureDetector surf(2500.);	//阈值
	//检测SURF特征
	surf.detect(image1, keypoints1);
	surf.detect(image2, keypoints2);
 
	//构造SURF描述子提取器
	SurfDescriptorExtractor surfDesc;
	//提取SURF描述子
	Mat descriptors1, descriptors2;
	surfDesc.compute(image1, keypoints1, descriptors1);
	surfDesc.compute(image2, keypoints2, descriptors2);
 
	//构造匹配器
	BruteForceMatcher<L2<float>> matcher;
	//匹配两幅图像的描述子
	vector<DMatch>matches;
	matcher.match(descriptors1, descriptors2, matches);
 
	nth_element(matches.begin(),	//初始位置
		matches.begin() + 7,	//排序元素的位置
		matches.end());		//终止位置
	//移除第25位之后所有的元素
	matches.erase(matches.begin() + 8, matches.end());
 
	Mat imageMatches;
	drawMatches(
		image1, keypoints1,	//第一幅图像及其特征点
		image2, keypoints2,	//第二幅图像及其特征点
		matches,	//匹配结果
		imageMatches,	//生成的图像
		Scalar(255, 255, 255));	//直线的颜色
 
	//转换KeyPoint类型到Point2f
	vector<Point2f>selPoints1, selPoints2;
	vector<int>pointIndexes1, pointIndexes2;
 
	for (std::vector<cv::DMatch>::const_iterator it = matches.begin();
		it != matches.end(); ++it)
	{
 
		// 获取所选匹配关键点的索引
		pointIndexes1.push_back(it->queryIdx);
		pointIndexes2.push_back(it->trainIdx);
	}
 
	KeyPoint::convert(keypoints1, selPoints1, pointIndexes1);
	KeyPoint::convert(keypoints2, selPoints2, pointIndexes2);
	//从7个矩阵中计算F矩阵
	Mat fundemental = findFundamentalMat(
		Mat(selPoints1),	//图1中的点
		Mat(selPoints2),	//图2中的点
		CV_FM_7POINT);	//使用7个点的方法
 
	//计算左图中点的极线 绘制在右图中 在右图中绘制对应的极线
	vector<Vec3f> lines1;
	computeCorrespondEpilines(
		Mat(selPoints1),	//图像点
		1,	//图1(也可以是2)
		fundemental,	//F矩阵
		lines1);	//一组极线
 
	//对于所有极线
	for (vector<Vec3f>::const_iterator it = lines1.begin(); it != lines1.end(); ++it)
	{
		//绘制第一列与最后一列之间的直线
		line(
			image2,//要绘制线段的图像
			Point(0, -(*it)[2] / (*it)[1]),//线段的起点
			Point(image2.cols, -((*it)[2] + (*it)[0] * image2.cols) / (*it)[1]), //线段的终点
			Scalar(255, 255, 255));//线段的颜色,通过一个Scalar对象定义
	}
 
	namedWindow("Left Image Epilines");
	imshow("Left Image Epilines", image2);
	imwrite("C:\\Users\\Administrator\\Desktop\\Left Image Epilines.jpg", image2);
 
	waitKey(0);
	return 0;
}

输入图像:
在这里插入图片描述
在这里插入图片描述
输出图像:
在这里插入图片描述
2、使用随机采样一致算法(RANSAC)进行图像匹配

1.什么是RANSAC?

RANSAC是RANdom Sample Consensus(随机采样一致性)的缩写。它是从一个观察数据集合中,估计模型参数(模型拟合)的迭代方法。它是一种随机的不确定算法,每次运算求出的结果可能不相同,但总能给出一个合理的结果,为了提高概率必须提高迭代次数。

2.算法详解

给定两个点p1与p2的坐标,确定这两点所构成的直线,要求对于输入的任意点p3,都可以判断它是否在该直线上。初中解析几何知识告诉我们,判断一个点在直线上,只需其与直线上任意两点点斜率都相同即可。实际操作当中,往往会先根据已知的两点算出直线的表达式(点斜式、截距式等等),然后通过向量计算即可方便地判断p3是否在该直线上。

生产实践中的数据往往会有一定的偏差。例如我们知道两个变量X与Y之间呈线性关系,Y=aX+b,我们想确定参数a与b的具体值。通过实验,可以得到一组X与Y的测试值。虽然理论上两个未知数的方程只需要两组值即可确认,但由于系统误差的原因,任意取两点算出的a与b的值都不尽相同。我们希望的是,最后计算得出的理论模型与测试值的误差最小。大学的高等数学课程中,详细阐述了最小二乘法的思想。通过计算最小均方差关于参数a、b的偏导数为零时的值。事实上,在很多情况下,最小二乘法都是线性回归的代名词。

遗憾的是,最小二乘法只适合与误差较小的情况。试想一下这种情况,假使需要从一个噪音较大的数据集中提取模型(比方说只有20%的数据时符合模型的)时,最小二乘法就显得力不从心了。例如下图,肉眼可以很轻易地看出一条直线(模式),但算法却找错了。

RANSAC算法的输入是一组观测数据(往往含有较大的噪声或无效点),一个用于解释观测数据的参数化模型以及一些可信的参数。RANSAC通过反复选择数据中的一组随机子集来达成目标。被选取的子集被假设为局内点,并用下述方法进行验证:

有一个模型适应于假设的局内点,即所有的未知参数都能从假设的局内点计算得出。
用1中得到的模型去测试所有的其它数据,如果某个点适用于估计的模型,认为它也是局内点。
如果有足够多的点被归类为假设的局内点,那么估计的模型就足够合理。
然后,用所有假设的局内点去重新估计模型(譬如使用最小二乘法),因为它仅仅被初始的假设局内点估计过。
最后,通过估计局内点与模型的错误率来评估模型。
上述过程被重复执行固定的次数,每次产生的模型要么因为局内点太少而被舍弃,要么因为比现有的模型更好而被选用。

#include<iostream>  
#include<opencv2/core/core.hpp>    
#include<opencv2/highgui/highgui.hpp> 
#include<opencv2/features2d/features2d.hpp>
#include <opencv2/nonfree/nonfree.hpp> 
#include<opencv2/legacy/legacy.hpp>  
#include<imgproc/imgproc.hpp>  
 
 
using namespace std;
using namespace cv;
 
 
class RobustMatcher{
 
private:
	//指向特征检测器的智能指针
	Ptr<FeatureDetector>detector;
	//指向描述子提取器的智能指针
	Ptr<DescriptorExtractor>extractor;
	float ratio;	//第一个以及第二个最近邻之间的最大比率
	bool refineF;	//是否改善F矩阵
	double distance;	//到极线的最小距离
	double confidence;	//置信等级(概率)
public:
	RobustMatcher() :ratio(0.65f), refineF(true), confidence(0.99), distance(3.0){
	
		//SURF为默认特征
		detector = new SurfFeatureDetector();
		extractor = new SurfDescriptorExtractor();
	}
 
	//设置特征检测器
	void setFeatureDetector(Ptr<FeatureDetector>&detect){
		detector = detect;
	}
 
	//设置描述子提取器
	void setDescriptorExtractor(Ptr<DescriptorExtractor>&desc){
		extractor = desc;
	}
	//设置最小距离极线RANSAC
	void setMinDistanceToEpipolar(double d) {
 
		distance = d;
	}
	//设置置信水平RANSAC
	void setConfidenceLevel(double c) {
		confidence = c;
	}
	void setRatio(float r) {
		ratio = r;
	}
	void refineFundamental(bool flag) {
		refineF = flag;
	}
 
	//使用对称性测试以及RANSAC匹配特征点
	//返回基础矩阵
	Mat match(Mat&image1,
		Mat&image2,	//输入图像
		//输出匹配及特征点
		vector<DMatch>&matches,
		vector<KeyPoint>&keypoints1,
		vector<KeyPoint>&keypoints2){
 
		//1a.测SURF特征
		detector->detect(image1, keypoints1);
		detector->detect(image2, keypoints2);
		//1b.取SURF描述子
		Mat descriptions1, descriptions2;
		extractor->compute(image1, keypoints1, descriptions1);
		extractor->compute(image2, keypoints2, descriptions2);
		//2.配两幅图像的描述子
		//创建匹配器
		BruteForceMatcher<L2<float>>matcher;
		//从图1->图2的K最近邻(k=2)
		vector<vector<DMatch>>matches1;
		matcher.knnMatch(descriptions1,descriptions2,//两幅图像的描述子
			matches1,	//匹配结果的向量(每项有两个值)
			2);	//返回两个最近邻
		//从图2->图1的k个最近邻(k=2)
		vector<vector<DMatch>>matches2;
		matcher.knnMatch(descriptions2, descriptions1,//两幅图像的描述子
			matches2,	//匹配结果的向量(每项有两个值)
			2);	//返回两个最近邻
 
		//3.移除NN比率大于阈值的匹配
		//清理图1->图2的匹配
		int removed = ratioTest(matches1);
		//清理图2->图1的匹配
		removed = ratioTest(matches2);
 
		//4.移除非对称的匹配
		vector<DMatch>symMatches;
		symmetryTest(matches1, matches2, symMatches);
		//5.使用RANSAC进行最终验证
		Mat fundemental = ransacTest(symMatches,keypoints1,keypoints2,matches);
		//返回找到的基础矩阵
		return fundemental;
	}
 
	//移除NN比率大于阈值的匹配 返回移除点的数目 对应的项被清零即尺寸将为0
	int ratioTest(vector<vector<DMatch>>& matches) {
		int removed = 0;
		//遍历所有匹配
		for (vector<vector<DMatch>>::iterator matchIterator = matches.begin();
			matchIterator != matches.end(); ++matchIterator) {
			// 如果识别两个最近邻
			if (matchIterator->size() > 1) {
				//检查距离比率
				if ((*matchIterator)[0].distance / (*matchIterator)[1].distance > ratio) {
					matchIterator->clear(); //移除匹配
					removed++;
				}
			}
			else { //不包含两个最近邻
 
				matchIterator->clear(); //移除匹配
				removed++;
			}
		}
		return removed;
	}

     //进行对称测试
	//在symMatches向量中插入对称匹配,
	void symmetryTest(const vector<vector<DMatch>>& matches1,
		const vector<vector<DMatch>>& matches2,
		vector<DMatch>& symMatches) {
		// 遍历图1->图2的所有匹配
		for (vector<vector<DMatch>>::const_iterator matchIterator1 = matches1.begin();
			matchIterator1 != matches1.end(); ++matchIterator1) {
			if (matchIterator1->size() < 2) //忽略被删除的匹配
				continue;
			//遍历图2->图1的所有匹配
			for (vector<vector<DMatch>>::const_iterator matchIterator2 = matches2.begin();
				matchIterator2 != matches2.end(); ++matchIterator2) {
				if (matchIterator2->size() < 2) //忽略被删除的匹配
					continue;
				// 对称性测试
				if ((*matchIterator1)[0].queryIdx == (*matchIterator2)[0].trainIdx &&
					(*matchIterator2)[0].queryIdx == (*matchIterator1)[0].trainIdx) {
					// 添加对称的匹配
					symMatches.push_back(DMatch((*matchIterator1)[0].queryIdx,
						(*matchIterator1)[0].trainIdx,
						(*matchIterator1)[0].distance));
					break; //图1->图2中的下一个匹配
				}
			}
		}
	}

     //使用基础矩阵移除不满足极性约束的匹配
	// 基于RANSAC识别优质匹配
	// 返回基础矩阵
	Mat ransacTest(const vector<DMatch>& matches,
		const vector<KeyPoint>& keypoints1,
		const vector<KeyPoint>& keypoints2,
		vector<DMatch>& outMatches) {
		//转换KeyPoint类型到Point2f
		vector<Point2f> points1, points2;
		for (vector<DMatch>::const_iterator it = matches.begin();
			it != matches.end(); ++it) {
			// 得到左边特征点的坐标
			float x = keypoints1[it->queryIdx].pt.x;
			float y = keypoints1[it->queryIdx].pt.y;
			points1.push_back(Point2f(x, y));
			// 得到右边特征点的左边
			x = keypoints2[it->trainIdx].pt.x;
			y = keypoints2[it->trainIdx].pt.y;
			points2.push_back(Point2f(x, y));
		}
		// 基于RANSAC计算F矩阵
		vector<uchar> inliers(points1.size(), 0);
		Mat fundemental = findFundamentalMat(
			Mat(points1), Mat(points2), // 匹配点
			inliers,      // 匹配状态 (inlier 或 outlier)  
			CV_FM_RANSAC, // RANSAC 方法
			distance,     // 到极线的距离
			confidence);  // 置信概率
		// 提取通过的匹配
		vector<uchar>::const_iterator itIn = inliers.begin();
		vector<DMatch>::const_iterator itM = matches.begin();
		// 遍历所有匹配
		for (; itIn != inliers.end(); ++itIn, ++itM) {
			if (*itIn) { // 为有效匹配
				outMatches.push_back(*itM);
			}
		}
		if (refineF) {
			// F矩阵将使用所有接受的匹配重新计算
			// 转换keypoints 类型到 Point2f 准备计算最终的F矩阵
			points1.clear();
			points2.clear();
			for (std::vector<DMatch>::const_iterator it = outMatches.begin();
				it != outMatches.end(); ++it) {
				// 得到左边特征点的坐标
				float x = keypoints1[it->queryIdx].pt.x;
				float y = keypoints1[it->queryIdx].pt.y;
				points1.push_back(Point2f(x, y));
				// 得到右边特征点的坐标
				x = keypoints2[it->trainIdx].pt.x;
				y = keypoints2[it->trainIdx].pt.y;
				points2.push_back(Point2f(x, y));
			}
 
			// 从所有接受的匹配点中计算8点F
			fundemental = findFundamentalMat(
				Mat(points1), Mat(points2), // 匹配
				CV_FM_8POINT); // 8点法
		}
		return fundemental;
	}
 
};
 
int main(){
 
	Mat image1 = imread("C:\\Users\\Administrator\\Desktop\\1_A.jpg",0);
	Mat image2 = imread("C:\\Users\\Administrator\\Desktop\\1_B.jpg",0);
 
	//准备匹配器
	RobustMatcher rmatcher;
	rmatcher.setConfidenceLevel(0.98);
	rmatcher.setMinDistanceToEpipolar(1.0);
	rmatcher.setRatio(0.65);
 
	Ptr<FeatureDetector> pfd = new SurfFeatureDetector(10);
	rmatcher.setFeatureDetector(pfd);
	//匹配两幅图像
	vector<DMatch>matches;
	vector<KeyPoint>keypoints1, keypoints2;
	Mat fundemental = rmatcher.match(image1, image2, matches, keypoints1, keypoints2);
 
	// 画匹配图像
	Mat imageMatches;
	drawMatches(image1, keypoints1,  // 第一幅图像及其特征点
		image2, keypoints2,  // 第二幅图像及其特征点
		matches,			// 匹配结果
		imageMatches,		// 生成的图像
		Scalar(255, 255, 255)); // 直线的颜色
	namedWindow("Matches");
	imshow("Matches", imageMatches);
 
	// 转换KeyPoint类型到Point2f
	std::vector<Point2f> points1, points2;
 
	for (std::vector<DMatch>::const_iterator it = matches.begin();
		it != matches.end(); ++it) {
 
		// 得到左边位置的特征点
		float x = keypoints1[it->queryIdx].pt.x;
		float y = keypoints1[it->queryIdx].pt.y;
		points1.push_back(Point2f(x, y));
		circle(
			image1,//输入的图像
			Point(x, y), //圆心位置
			3,//圆的半径
			Scalar(255, 255, 255), //圆的颜色
			3);//圆形轮廓的粗细
		// 得到右边位置的特征点
		x = keypoints2[it->trainIdx].pt.x;
		y = keypoints2[it->trainIdx].pt.y;
		circle(
			image2, //输入的图像
			Point(x, y), //圆心位置
			3, //圆的半径
			Scalar(255, 255, 255), //圆的颜色
			3);//圆形轮廓的粗细
		points2.push_back(Point2f(x, y));
	}
 
	// 画极线
	std::vector<Vec3f> lines1;
	computeCorrespondEpilines(Mat(points1), 1, fundemental, lines1);
 
	for (vector<Vec3f>::const_iterator it = lines1.begin();
		it != lines1.end(); ++it) {
 
		line(image2, //要划的线所在的图像;
			Point(0, -(*it)[2] / (*it)[1]),//直线起点
			Point(image2.cols, -((*it)[2] + (*it)[0] * image2.cols) / (*it)[1]),//直线终点
			Scalar(255, 255, 255));//直线的颜色
	}
 
	std::vector<Vec3f> lines2;
	computeCorrespondEpilines(Mat(points2), 2, fundemental, lines2);
 
	for (vector<Vec3f>::const_iterator it = lines2.begin();
		it != lines2.end(); ++it) {
 
		line(image1, //要划的线所在的图像;
			Point(0, -(*it)[2] / (*it)[1]),//直线起点
			Point(image1.cols, -((*it)[2] + (*it)[0] * image1.cols) / (*it)[1]),//直线终点
			Scalar(255, 255, 255));//直线的颜色
	}
 
	// 显示带有极线约束图片
	namedWindow("Right Image Epilines (RANSAC)");
	imshow("Right Image Epilines (RANSAC)", image1);
	namedWindow("Left Image Epilines (RANSAC)");
	imshow("Left Image Epilines (RANSAC)", image2);
 
	waitKey();
	return 0;
}

测试失败:
“System.AccessViolationException”类型的未经处理的异常出现在 RANSAC进行图像匹配.exe 中。

其他信息: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏。

3、计算两幅图之间的单应矩阵
单应性矩阵:
描述的是共面点在两个相机视图下的像素点的约束关系,描述的是点与点之间的约束关系,使用单应矩阵可以找到像点在另一幅图像上对应点的确切位置。

即当已知两幅图像间的单应性矩阵H时,可以通过计算对应像素点的坐标。

特殊情况1:当相机只纯旋转,不平移时,即使不共面,也可以使用单应性来描述,因为此时基础矩阵F为0。

特殊情况2:当相机的平移距离相对于场景的深度较小的时候,也可以使用单应矩阵H来描述约束关系。

#include<iostream>  
#include<opencv2/core/core.hpp>    
#include<opencv2/highgui/highgui.hpp> 
#include<opencv2/features2d/features2d.hpp>
#include "opencv2/nonfree/nonfree.hpp"  
#include<opencv2/legacy/legacy.hpp>  
#include<imgproc/imgproc.hpp>  
 
using namespace std;
using namespace cv;

class RobustMatcher{
 
private:
	//指向特征检测器的智能指针
	Ptr<FeatureDetector>detector;
	//指向描述子提取器的智能指针
	Ptr<DescriptorExtractor>extractor;
	float ratio;	//第一个以及第二个最近邻之间的最大比率
	bool refineF;	//是否改善F矩阵
	double distance;	//到极线的最小距离
	double confidence;	//置信等级(概率)
public:
	RobustMatcher() :ratio(0.65f), refineF(true), confidence(0.99), distance(3.0){
	
		//SURF为默认特征
		detector = new SurfFeatureDetector();
		extractor = new SurfDescriptorExtractor();
	}
 
	//设置特征检测器
	void setFeatureDetector(Ptr<FeatureDetector>&detect){
		detector = detect;
	}
 
	//设置描述子提取器
	void setDescriptorExtractor(Ptr<DescriptorExtractor>&desc){
		extractor = desc;
	}
	//设置最小距离极线RANSAC
	void setMinDistanceToEpipolar(double d) {
 
		distance = d;
	}
	//设置置信水平RANSAC
	void setConfidenceLevel(double c) {
		confidence = c;
	}
	void setRatio(float r) {
		ratio = r;
	}
	void refineFundamental(bool flag) {
		refineF = flag;
	}
 
	//使用对称性测试以及RANSAC匹配特征点
	//返回基础矩阵
	Mat match(Mat&image1,
		Mat&image2,	//输入图像
		//输出匹配及特征点
		vector<DMatch>&matches,
		vector<KeyPoint>&keypoints1,
		vector<KeyPoint>&keypoints2){
 
		//1a测SURF特征
		detector->detect(image1, keypoints1);
		detector->detect(image2, keypoints2);
		//1b取SURF描述子
		Mat descriptions1, descriptions2;
		extractor->compute(image1, keypoints1, descriptions1);
		extractor->compute(image2, keypoints2, descriptions2);
		//2.配两幅图像的描述子
		//创建匹配器
		BruteForceMatcher<L2<float>>matcher;
		//从图1->图2的K最近邻(k=2)
		vector<vector<DMatch>>matches1;
		matcher.knnMatch(descriptions1,descriptions2,
			matches1,	//匹配结果的向量(每项有两个值)
			2);	//返回两个最近邻
		//从图2->图1的k个最近邻(k=2)
		vector<vector<DMatch>>matches2;
		matcher.knnMatch(descriptions2, descriptions1,
			matches2,	//匹配结果的向量(每项有两个值)
			2);	//返回两个最近邻
 
		//3.移除NN比率大于阈值的匹配
		//清理图1->图2的匹配
		int removed = ratioTest(matches1);
		//清理图2->图1的匹配
		removed = ratioTest(matches2);
 
		//4.移除非对称的匹配
		vector<DMatch>symMatches;
		symmetryTest(matches1, matches2, symMatches);
		//5.使用RANSAC进行最终验证
		Mat fundemental = ransacTest(symMatches,keypoints1,keypoints2,matches);
		//返回找到的基础矩阵
		return fundemental;
	}
 
	//移除NN比率大于阈值的匹配 返回移除点的数目 对应的项被清零即尺寸将为0
	int ratioTest(vector<vector<DMatch>>& matches) {
		int removed = 0;
		//遍历所有匹配
		for (vector<vector<DMatch>>::iterator matchIterator = matches.begin();
			matchIterator != matches.end(); ++matchIterator) {
			// 如果识别两个最近邻
			if (matchIterator->size() > 1) {
				//检查距离比率
				if ((*matchIterator)[0].distance / (*matchIterator)[1].distance > ratio) {
					matchIterator->clear(); //移除匹配
					removed++;
				}
			}
			else { //不包含两个最近邻
 
				matchIterator->clear(); //移除匹配
				removed++;
			}
		}
		return removed;
	}
 
	//在symMatches向量中插入对称匹配
	void symmetryTest(const vector<vector<DMatch>>& matches1,
		const vector<vector<DMatch>>& matches2,
		vector<DMatch>& symMatches) {
		// 遍历图1->图2的所有匹配
		for (vector<vector<DMatch>>::const_iterator matchIterator1 = matches1.begin();
			matchIterator1 != matches1.end(); ++matchIterator1) {
			if (matchIterator1->size() < 2) //忽略被删除的匹配
				continue;
			//遍历图2->图1的所有匹配
			for (vector<vector<DMatch>>::const_iterator matchIterator2 = matches2.begin();
				matchIterator2 != matches2.end(); ++matchIterator2) {
				if (matchIterator2->size() < 2) //忽略被删除的匹配
					continue;
				// 对称性测试
				if ((*matchIterator1)[0].queryIdx == (*matchIterator2)[0].trainIdx &&
					(*matchIterator2)[0].queryIdx == (*matchIterator1)[0].trainIdx) {
					// 添加对称的匹配
					symMatches.push_back(DMatch((*matchIterator1)[0].queryIdx,
						(*matchIterator1)[0].trainIdx,
						(*matchIterator1)[0].distance));
					break; //图1->图2中的下一个匹配
				}
			}
		}
	}
 
	// 基于RANSAC识别优质匹配
	// 返回基础矩阵
	Mat ransacTest(const vector<DMatch>& matches,
		const vector<KeyPoint>& keypoints1,
		const vector<KeyPoint>& keypoints2,
		vector<DMatch>& outMatches) {
		//转换KeyPoint类型到Point2f
		vector<Point2f> points1, points2;
		for (vector<DMatch>::const_iterator it = matches.begin();
			it != matches.end(); ++it) {
			// 得到左边特征点的坐标
			float x = keypoints1[it->queryIdx].pt.x;
			float y = keypoints1[it->queryIdx].pt.y;
			points1.push_back(Point2f(x, y));
			// 得到右边特征点的左边
			x = keypoints2[it->trainIdx].pt.x;
			y = keypoints2[it->trainIdx].pt.y;
			points2.push_back(Point2f(x, y));
		}
		// 基于RANSAC计算F矩阵
		vector<uchar> inliers(points1.size(), 0);
		Mat fundemental = findFundamentalMat(
			Mat(points1), Mat(points2), // 匹配点
			inliers,      // 匹配状态 (inlier 或 outlier)  
			CV_FM_RANSAC, // RANSAC 方法
			distance,     // 到极线的距离
			confidence);  // 置信概率
		// 提取通过的匹配
		vector<uchar>::const_iterator itIn = inliers.begin();
		vector<DMatch>::const_iterator itM = matches.begin();
		// 遍历所有匹配
		for (; itIn != inliers.end(); ++itIn, ++itM) {
			if (*itIn) { // 为有效匹配
				outMatches.push_back(*itM);
			}
		}
		if (refineF) {
			// F矩阵将使用所有接受的匹配重新计算
			// 转换keypoints 类型到 Point2f 准备计算最终的F矩阵
			points1.clear();
			points2.clear();
			for (std::vector<DMatch>::const_iterator it = outMatches.begin();
				it != outMatches.end(); ++it) {
				// 得到左边特征点的坐标
				float x = keypoints1[it->queryIdx].pt.x;
				float y = keypoints1[it->queryIdx].pt.y;
				points1.push_back(Point2f(x, y));
				// 得到右边特征点的坐标
				x = keypoints2[it->trainIdx].pt.x;
				y = keypoints2[it->trainIdx].pt.y;
				points2.push_back(Point2f(x, y));
			}
 
			// 从所有接受的匹配点中计算8点F
			fundemental = findFundamentalMat(
				Mat(points1), Mat(points2), // 匹配
				CV_FM_8POINT); // 8点法
		}
		return fundemental;
	}
 
};


int main()
{
	Mat image1 = imread("C:\\Users\\Administrator\\Desktop\\1_A.jpg", 0);
	Mat image2 = imread("C:\\Users\\Administrator\\Desktop\\1_B.jpg", 0);
	if (!image1.data || !image2.data)
		return 0;
 
	//准备匹配器
	RobustMatcher rmatcher;
	rmatcher.setConfidenceLevel(0.98);
	rmatcher.setMinDistanceToEpipolar(1.0);
	rmatcher.setRatio(0.65f);
	Ptr<FeatureDetector> pfd = new SurfFeatureDetector(10);
	rmatcher.setFeatureDetector(pfd);
 
	//匹配两幅图像
	std::vector<DMatch> matches;
	std::vector<KeyPoint> keypoints1, keypoints2;
	Mat fundemental = rmatcher.match(image1, image2, matches, keypoints1, keypoints2);
 
	// 画匹配图像
	Mat imageMatches;
	drawMatches(image1, keypoints1,  // 第一幅图像及其特征点
		image2, keypoints2,  //  第二幅图像及其特征点
		matches,			// 匹配结果
		imageMatches,		// 生成的图像
		Scalar(255, 255, 255)); // 直线的颜色
	namedWindow("Matches");
	imshow("Matches", imageMatches);
	imwrite("C:\\Users\\Administrator\\Desktop\\Matches.jpg", imageMatches);
 
	// 转换KeyPoint类型到Point2f
	std::vector<Point2f> points1, points2;
	for (std::vector<DMatch>::const_iterator it = matches.begin();
		it != matches.end(); ++it) {
 
		// 得到左边位置的特征点
		float x = keypoints1[it->queryIdx].pt.x;
		float y = keypoints1[it->queryIdx].pt.y;
		points1.push_back(Point2f(x, y));
		// 得到右边位置的特征点
		x = keypoints2[it->trainIdx].pt.x;
		y = keypoints2[it->trainIdx].pt.y;
		points2.push_back(Point2f(x, y));
	}
 
	//找到两个图之间的单应矩阵
	std::vector<uchar> inliers(points1.size(), 0);
	Mat homography = findHomography(
		Mat(points1), Mat(points2), // 对应的点集
		inliers,	// 输出的正确值
		CV_RANSAC,	// RANSAC 算法
		1.);	    // 到反投影点的最大距离
 
	// 绘制inlier点
	std::vector<Point2f>::const_iterator itPts = points1.begin();
	std::vector<uchar>::const_iterator itIn = inliers.begin();
	while (itPts != points1.end()) {
 
		// 在每个inlier位置画圆
		if (*itIn)
			circle(image1,//画圆的图像
			*itPts,//圆心
			3, //半径
			Scalar(255, 255, 255),//颜色
			2);//线的宽度
 
		++itPts;
		++itIn;
	}
 
	itPts = points2.begin();
	itIn = inliers.begin();
	while (itPts != points2.end()) {
 
		// 在每个inlier位置画圆
		if (*itIn)
			circle(image2, //画圆的图像
			*itPts,//圆心
			3, //半径
			Scalar(255, 255, 255), //颜色
			2);//线的宽度
 
		++itPts;
		++itIn;
	}
 
	namedWindow("Image 1 Homography Points");
	imshow("Image 1 Homography Points", image1);
	imwrite("C:\\Users\\Administrator\\Desktop\\Image 1 Homography Points.jpg", image1);
	namedWindow("Image 2 Homography Points");
	imshow("Image 2 Homography Points", image2);
	imwrite("C:\\Users\\Administrator\\Desktop\\Image 2 Homography Points.jpg", image2);

     //对图像进行透视变换,就是变形
	// 歪曲image2 到 image1
	Mat result;
	warpPerspective(image2, // 输入
		result,			// 输出
		homography,		// 透视变换的矩阵,homography
		Size(2 * image2.cols, image2.rows)); // 输出图像的尺寸
 
	// 赋值图2到整幅图像的前半部分
	Mat half(result, Rect(0, 0, image1.cols, image1.rows));//Rect(0,0,image.cols,image.rows),防止Rect越界
	image1.copyTo(half);//复制图1到图2的ROI区域
 
	namedWindow("After warping");
	imshow("After warping", result);
	imwrite("C:\\Users\\Administrator\\Desktop\\After warping.jpg",result);
 
	waitKey();
	return 0;
}

输入图像:
在这里插入图片描述
在这里插入图片描述
输出图像:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
!!!最后一幅图像不对,不知道什么原因。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值