1、计算一对图像的基础矩阵
#include
#include
#include
#include
#include "opencv2/nonfree/nonfree.hpp"
#include
#include
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
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include "opencv2/nonfree/nonfree.hpp"
#include
#include
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;
}