两张二维图片的配准

#include  //标准输入输出流
#include  //PCL的PCD格式文件的输入输出头文件
#include  //PCL的PCD格式文件的输入输出头文件
#include  //PCL对各种格式的点的支持头文件
#include 
//预处理
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//特征提取
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//配准
#include    //配准模块头文件
//重建
#include 
#include 
#include 
using namespace cv;
using namespace std;

//计算原始图像点为在经过矩阵变换后再目标图像上对应位置
Point2f getTransformPoint(const Point2f originalPoint, const Mat &transformMaxtri)
{
	Mat originelP, targetP;
	originelP = (Mat_(3, 1) << originalPoint.x, originalPoint.y, 1.0);
	targetP = transformMaxtri*originelP;
	float x = targetP.at(0, 0) / targetP.at(2, 0);
	float y = targetP.at(1, 0) / targetP.at(2, 0);
	return Point2f(x, y);
}
//计算原始图像的点经过矩阵变换后再目标图像上的对应位置
//Point2f getTransformPoin(const Point2f OriginalPoint, const Mat &tranformMatrix)

int main(){
	Mat ImgL = imread("18_0.jpg"), ImgR = imread("17_0.jpg");
	if (ImgL.data == NULL || ImgR.data == NULL)
		return 0;

	////转换为灰度图
	Mat grayL, grayR;//创建灰度图矩阵对象
	grayL = ImgL; grayR = ImgR;
	
	//获取两幅图像的共同特征点 SURF Speed Up Rubost Feature
	SurfFeatureDetector detector(400);
	vector KPL, KPR; //创建两幅图像的灰度图对象
	detector.detect(grayL, KPL);
	detector.detect(grayR, KPR);//根据灰度值的极值探测两幅图像中的关键点

	SurfDescriptorExtractor extractor;
	Mat descriptorL, descriptorR;//创建特征描述子对象 特征点转为特征向量(就是所谓的特征描述子)最终得到的就是一个特征向量构成的特征矩阵,特征点个数等于其矩阵行数
	extractor.compute(grayL, KPL, descriptorL);
	extractor.compute(grayR, KPR, descriptorR);//计算两幅图像的特征描述子

	//寻找的关键点匹配对可能存在错误的点对,所以进行匹配点对的优化,可以选择匹配阶段进行剔除错误匹配点对(FnnMatch方法,
	//也可以选择在计算单应性矩阵步骤中进行有效匹配点对的识别 利用计算转移矩阵方法中的Led_Median或者FM_RANSAC算法实现,参数mask返回识别结果
	//匹配
	FlannBasedMatcher matcher;
	vector matches,goodMatch;//DMatch 一个结构体,储存点匹配对,待询问的(左图像)描述子的索引、训练的(右图像)描述子的索引,两个描述子之间的距离
	matcher.match(descriptorL, descriptorR, matches);//寻找descriptorL最好的点匹配对 匹配对之间的距离是特征向量之间的欧式距离之类的
	sort(matches.begin(), matches.begin() + descriptorL.rows);//将寻找到的匹配对进行排序,应该是按照匹配点之间的距离由小到大 可是距离如何计算的?
	int matchNum = 10;// descriptorL.rows;
	//if (matchNum > 15) matchNum = 15;//15是可以自主设定,选择前面匹配最好的matchNum个匹配对
	vector leftPoints, rightPoints;//用于储存两幅图像中的匹配度好的关键点
	for(int i = 0; i < matchNum; i++){
		goodMatch.push_back(matches[i]);
		leftPoints.push_back(KPL[matches[i].queryIdx].pt);
		rightPoints.push_back(KPR[matches[i].trainIdx].pt);//根究前面排序的结果
	}//注意 这里的goodMatch中储存的匹配点对不一定是有效的随意接下来要利用RANSAC算法进行检验有效点对
	
	//显示两幅图像的匹配效果好的点对链接图
	Mat firstMatch;
	drawMatches(ImgL, KPL, ImgR, KPR, goodMatch, firstMatch);
	imshow("the image including good matching points", firstMatch);
	imwrite("特征点连接图.jpg", firstMatch);

	//从找到的较佳匹配点对信息中获取左右两张图片的投影关系(投影矩阵)3*3
	vector mask;
	Mat homo = findHomography(leftPoints, rightPoints, mask, FM_RANSAC);//根据匹配度较好的关键点的位置坐标计算最匹配的投影矩阵 3*3 
	cout << homo;
	
	//显示最终使用的有效特征点对
	Mat SecondMatch;//储存=图像信息 矩阵形式
	vector	BetterMatch;
	for (int i = 0; i < leftPoints.size(); i++){
		if (mask[i] ) BetterMatch.push_back(goodMatch[i]);
	}
	drawMatches(ImgL, KPL, ImgR, KPR, BetterMatch, SecondMatch);
	//namedWindow("the image including good matching points  except erros", 0);
	imshow("the image including good matching points  except erros", SecondMatch);
	//waitKey(30);
	imwrite("排除错误匹配点对的特征点连接图.jpg", SecondMatch);

	Mat shftMat = (Mat_(3, 3) << 1.0, 0,ImgL.cols, 0, 1.0, 0, 0, 0, 1.0);//平移矩阵(沿x轴方向),旋转之后,某些点会移动到负轴上,这一步平移保证旋转之后显示的完整性
	Mat shftHomo = shftMat* homo;//按照先平移再旋转 矩阵相乘从右向左计算

	//获取较好的匹配点对在原始图像和矩阵变换后的图像中的对应位置,用于图像拼接点的定位
	Point2f Pl, Pr, Pt;//储存xy下标,表示位置的变量
	Pl = KPL[matches[0].queryIdx].pt; //获取匹配度最高的点对对应左图中的点的坐标
	Pt = getTransformPoint(Pl,shftHomo);//Pl扭转后的坐标 利用上面的旋转矩阵
	Pr = KPR[matches[0].trainIdx].pt;//获取匹配度最高的点对对应右图中的点的坐标

	//拼接图像 二维图像配准
	Mat ImgMatched;
	warpPerspective(ImgL, ImgMatched, shftHomo, Size(ImgL.cols + ImgR.cols, ImgR.rows));//通过转换矩阵,扭转左边的图像,结果储存到目标变量中  Size中两个参数分别为width和height
	//namedWindow("扭转后的图像",0);
	imwrite("扭转后的左图像.jpg",ImgMatched);
	//waitKey(30);
	
	//在最强匹配点左侧的重叠区域进行累加,是衔接稳定过渡,消除突变
	Mat leftOverlap, rightOverlap; //图1和图2的部分重叠部分	
	leftOverlap = ImgMatched(Rect(Point(Pt.x - Pr.x, 0), Point(Pt.x, ImgR.rows)));//
	rightOverlap = ImgR(Rect(0, 0, leftOverlap.cols, leftOverlap.rows));
	//waitKey(30);
	cout << "rightOverlap " << rightOverlap.size() << endl;
	Mat leftROICopy =leftOverlap.clone();  //复制一份图1的重叠部分
	for (int i = 0; i(i, j)[0] = (1 - weight)*leftROICopy.at(i, j)[0] + weight*rightOverlap.at(i, j)[0];
			leftOverlap.at(i, j)[1] = (1 - weight)*leftROICopy.at(i, j)[1] + weight*rightOverlap.at(i, j)[1];
			leftOverlap.at(i, j)[2] = (1 - weight)*leftROICopy.at(i, j)[2] + weight*rightOverlap.at(i, j)[2];
		}
	}//通过坐标传递改变扭转后的左图中重叠部分的像素值 这是重叠区域的部分的平滑重叠,其余均使用有图图像中的部分
	Mat M1 = ImgR(Rect(Point(leftOverlap.cols, 0), Point(ImgR.cols,ImgR.rows)));	 //提取图2中剩余得的部分
	//Mat M1 = ImgR(Rect(Point(Pb.x, 0), Point(ImgR.cols, ImgR.rows)));
	M1.copyTo(Mat(ImgMatched, Rect(Pt.x, 0, M1.cols, ImgR.rows)));//将M1 赋值到ImgMatched对象中的Rect表示的位置中

	//显示拼接后的图像
	imshow("matched image", ImgMatched);
	waitKey(30);
	imwrite("拼接效果图.jpg", ImgMatched);
	system("pause");
	return 0; 

}

 

你可能感兴趣的:(C++,三维重建,三维重建之基本知识,图像处理)