OpenCV 估算图像的投影关系：基础矩阵和RANSAC

0
0
0
1. 云栖社区>
2. 博客>
3. 正文

## OpenCV 估算图像的投影关系：基础矩阵和RANSAC

jacoby 2018-10-07 21:01:00 浏览1500

#### 1.基础矩阵

``````/********************************************************************
* Created by 杨帮杰 on 10/7/18
* Right to use this code in any way you want without
* warranty, support or any guarantee of it working
* E-mail: yangbangjie1998@qq.com
* Association: SCAU 华南农业大学
********************************************************************/

#include <iostream>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/objdetect.hpp>
#include <opencv2/xfeatures2d.hpp>

#define CHURCH01 "/home/jacob/图片/images/church01.jpg"
#define CHURCH02 "/home/jacob/图片/images/church02.jpg"
#define CHURCH03 "/home/jacob/图片/images/church03.jpg"

using namespace cv;
using namespace std;

int main()
{
if (!image1.data || !image2.data)
return 0;

imshow("Right Image",image1);
imshow("Left Image",image2);

//检测并匹配SIFT描述子
vector<KeyPoint> keypoints1;
vector<KeyPoint> keypoints2;
Mat descriptors1, descriptors2;

Ptr<Feature2D> ptrFeature2D = xfeatures2d::SIFT::create(74);

ptrFeature2D->detectAndCompute(image1, noArray(), keypoints1, descriptors1);
ptrFeature2D->detectAndCompute(image2, noArray(), keypoints2, descriptors2);

cout << "Number of SIFT points (1): " << keypoints1.size() << endl;
cout << "Number of SIFT points (2): " << keypoints2.size() << endl;

Mat imageKP;
drawKeypoints(image1,keypoints1,imageKP,
Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("Right SIFT Features",imageKP);

drawKeypoints(image2,keypoints2,imageKP,
Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("Left SIFT Features",imageKP);

//使用交叉验证
BFMatcher matcher(NORM_L2,true);

vector<DMatch> matches;
matcher.match(descriptors1,descriptors2, matches);

cout << "Number of matched points: " << matches.size() << endl;

// 手工选择配对成功的七组匹配，有点麻烦
vector<DMatch> selMatches;

selMatches.push_back(matches[8]);
selMatches.push_back(matches[21]);
selMatches.push_back(matches[15]);
selMatches.push_back(matches[17]);
selMatches.push_back(matches[22]);
selMatches.push_back(matches[27]);
selMatches.push_back(matches[29]);

Mat imageMatches;
drawMatches(image1,keypoints1,  // 1st image and its keypoints
image2,keypoints2,  // 2nd image and its keypoints
selMatches,         // the selected matches
imageMatches,       // the image produced
Scalar(255,255,255),
Scalar(255,255,255),
vector<char>(),
2);

imshow("Matches",imageMatches);

//根据筛选出的匹配得到对应点的index
vector<int> pointIndexes1;
vector<int> pointIndexes2;
for (vector<DMatch>::const_iterator it= selMatches.begin();
it!= selMatches.end(); ++it)
{
pointIndexes1.push_back(it->queryIdx);
pointIndexes2.push_back(it->trainIdx);
}

//将KeyPoint类型转换为Point2f类型
vector<Point2f> selPoints1, selPoints2;
KeyPoint::convert(keypoints1,selPoints1,pointIndexes1);
KeyPoint::convert(keypoints2,selPoints2,pointIndexes2);

//在筛选出的点的位置上画圈
vector<Point2f>::const_iterator it= selPoints1.begin();
while (it!=selPoints1.end())
{
circle(image1,*it,3,Scalar(255,255,255),2);
++it;
}

it= selPoints2.begin();
while (it!=selPoints2.end())
{
circle(image2,*it,3,Scalar(255,255,255),2);
++it;
}

//根据7对匹配来计算基础矩阵
Mat fundamental= findFundamentalMat(
selPoints1, // points in first image
selPoints2, // points in second image
FM_7POINT);       // 7-point method

cout << "F-Matrix size= " << fundamental.rows << "x" << fundamental.cols << endl;

//根据基础矩阵的匹配点计算对极线
vector<Vec3f> lines1;
computeCorrespondEpilines(
selPoints1, // image points
1,                   // in image 1 (can also be 2)
fundamental, // F matrix
lines1);     // vector of epipolar lines

//画出左右图像的对极线
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));
}

vector<Vec3f> lines2;
computeCorrespondEpilines(Mat(selPoints2),2,fundamental,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));
}

//拼接两幅图像
Mat both(image1.rows,image1.cols+image2.cols, CV_8U);
image1.copyTo(both.colRange(0, image1.cols));
image2.copyTo(both.colRange(image1.cols, image1.cols+image2.cols));

imshow("Epilines",both);

waitKey();
return 0;
}
``````

7点法估算基础矩阵

#### 2.RANSAC（随机采样一致性）

n个点至少有一个是外点的概率

``````/********************************************************************
* Created by 杨帮杰 on 10/7/18
* Right to use this code in any way you want without
* warranty, support or any guarantee of it working
* E-mail: yangbangjie1998@qq.com
* Association: SCAU 华南农业大学
********************************************************************/

#include <iostream>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/calib3d.hpp>

#define CHURCH01 "/home/jacob/图片/images/church01.jpg"
#define CHURCH02 "/home/jacob/图片/images/church02.jpg"
#define CHURCH03 "/home/jacob/图片/images/church03.jpg"

#define IS_REFINE_FUNDA 1
#define IS_REFINE_MATCHES 1

using namespace cv;
using namespace std;

int main()
{

if (!image1.data || !image2.data)
return 0;

//检测并匹配SIFT描述子
vector<KeyPoint> keypoints1;
vector<KeyPoint> keypoints2;
Mat descriptors1, descriptors2;

Ptr<Feature2D> ptrFeature2D = xfeatures2d::SIFT::create(100);

ptrFeature2D->detectAndCompute(image1, noArray(), keypoints1, descriptors1);
ptrFeature2D->detectAndCompute(image2, noArray(), keypoints2, descriptors2);

cout << "Number of SIFT points (1): " << keypoints1.size() << endl;
cout << "Number of SIFT points (2): " << keypoints2.size() << endl;

Mat imageKP;
drawKeypoints(image1,keypoints1,imageKP,
Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("Right SIFT Features",imageKP);

drawKeypoints(image2,keypoints2,imageKP,
Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("Left SIFT Features",imageKP);

//使用交叉验证
BFMatcher matcher(NORM_L2,true);

vector<DMatch> matches;
matcher.match(descriptors1,descriptors2, matches);

vector<Point2f> points1, points2;

for (vector<DMatch>::const_iterator it= matches.begin();
it!= matches.end(); ++it)
{
points1.push_back(keypoints1[it->queryIdx].pt);
points2.push_back(keypoints2[it->trainIdx].pt);
}

//使用RANSAC算法计算基础矩阵
//inliers相当于一个掩膜
vector<uchar> inliers(points1.size(),0);
Mat fundamental= findFundamentalMat(
points1,points2, // matching points
inliers,         // match status (inlier or outlier)
FM_RANSAC,
1.0,      // distance to epipolar line
0.99     // confidence probability
);

//提取合格的匹配项
vector<uchar>::const_iterator itIn= inliers.begin();
vector<DMatch>::const_iterator itM= matches.begin();
vector<DMatch> outMatches;
// for all matches
for ( ;itIn!= inliers.end(); ++itIn, ++itM)
{
if (*itIn == true)
{
outMatches.push_back(*itM);
}
}

if (IS_REFINE_FUNDA || IS_REFINE_MATCHES)
{
//使用RANSAC得出的高质量匹配点再次估算基础矩阵
points1.clear();
points2.clear();

//得到高质量匹配点的坐标
for (vector<DMatch>::const_iterator it= outMatches.begin();
it!= outMatches.end(); ++it)
{
points1.push_back(keypoints1[it->queryIdx].pt);
points2.push_back(keypoints2[it->trainIdx].pt);
}

//用八点法计算基础矩阵
fundamental= findFundamentalMat(
points1,points2, // matching points
FM_8POINT); // 8-point method

if (IS_REFINE_MATCHES)
{
//用基础矩阵来矫正匹配点的位置
vector<Point2f> newPoints1, newPoints2;

correctMatches(fundamental,             // F matrix
points1, points2,        // original position
newPoints1, newPoints2); // new position

for (int i=0; i< points1.size(); i++)
{

cout << "(" << keypoints1[outMatches[i].queryIdx].pt.x
<< "," << keypoints1[outMatches[i].queryIdx].pt.y
<< ") -> ";

cout << "(" << newPoints1[i].x
<< "," << newPoints1[i].y << endl;

cout << "(" << keypoints2[outMatches[i].trainIdx].pt.x
<< "," << keypoints2[outMatches[i].trainIdx].pt.y
<< ") -> ";

cout << "(" << newPoints2[i].x
<< "," << newPoints2[i].y << endl;

keypoints1[outMatches[i].queryIdx].pt.x= newPoints1[i].x;
keypoints1[outMatches[i].queryIdx].pt.y= newPoints1[i].y;

keypoints2[outMatches[i].trainIdx].pt.x= newPoints2[i].x;
keypoints2[outMatches[i].trainIdx].pt.y= newPoints2[i].y;
}
}
}

Mat imageMatches;
drawMatches(image1,keypoints1,  // 1st image and its keypoints
image2,keypoints2,  // 2nd image and its keypoints
outMatches,         // the matches
imageMatches,       // the image produced
Scalar(255,255,255),  // color of the lines
Scalar(255,255,255),  // color of the keypoints
vector<char>(),
2);

imshow("Matches",imageMatches);

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(keypoints1[it->queryIdx].pt);
circle(image1,Point(x,y),3,Scalar(255,255,255),3);

//得到右图像特征点的位置并画圆
x= keypoints2[it->trainIdx].pt.x;
y= keypoints2[it->trainIdx].pt.y;
points2.push_back(keypoints2[it->trainIdx].pt);
circle(image2,Point(x,y),3,Scalar(255,255,255),3);

}

//画出两幅图像的对极线
vector<Vec3f> lines1;
computeCorrespondEpilines(points1,1,fundamental,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));
}

vector<Vec3f> lines2;
computeCorrespondEpilines(points2,2,fundamental,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));
}

imshow("Right Image Epilines (RANSAC)",image1);
imshow("Left Image Epilines (RANSAC)",image2);

waitKey();
return 0;
}
``````

RANSAC得到的匹配项

References:
SLAM入门之视觉里程计(4)：基础矩阵的估计
SLAM入门之视觉里程计(3)：两视图对极约束 基础矩阵
opencv计算机视觉编程攻略（第三版） —— Robert Laganiere

jacoby
+ 关注