#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;
}