[再次续]关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

参考博客:

https://blog.csdn.net/tina_ttl/article/details/52749542

参考:

《计算机视觉中的多视几何》 ·第八章·对极几何和基本矩阵 

该作者的几篇好文章:(链接可点击)

多视几何:齐次坐标

多视几何:摄像机模型的推导

多视几何:对极几何的代数表示--基本矩阵F

对极几何基本概念

matlab·计算机视觉·工具箱

如何利用旋转矩阵得到四元数

conic section

参考:

1.美国北卡罗来纳大学教堂山分校(UNC)的Visual 3D Modeling from Images的课程notes,详细内容见该课程目录页,有一些关于摄影几何基本知识、摄像机模型、多视几何等的基本概念的介绍,浅显易懂,特别是配了很多特别棒的图,易于理解!

2.University of California 的计算机视觉课程CSE 252B: Computer Vision II

3.多视几何教程 Richard Hartley和Andrew Zisserman的多视几何的MATLAB代码,只有书中部分内容对应的代码


在上上一次的文章结尾,我们说了F矩阵还有一种剔除的表达形式

关于opencv2.4.10-3.3.1左右版本的特征点剔除与显示问题

这里我们完成它

先介绍一下:

1. 什么是对极几何·粗略概念

提到对极几何,一定是对二幅图像而言,对极几何实际上是“两幅图像之间的对极几何”,它是图像平面以基线为轴的平面束的交的几何(这里的基线是指连接摄像机中心的直线),以下图为例:对极几何描述的是左右两幅图像(点x和x’对应的图像)与以CC’为轴的平面束的交的几何!

直线CC’为基线,以该基线为轴存在一个平面束,该平面束与两幅图像平面相交,下图给出了该平面束的直观形象,可以看到,该平面束中不同平面与两幅图像相交于不同直线; 

上图中的灰色平面π,只是过基线的平面束中的一个平面(当然,该平面才是平面束中最重要的、也是我们要研究的平面);

2. 对极几何相关的一个重要约束·5点共面约束

仍以上面贴出的图像为例,此处重复贴出,空间点X在两幅图像中的像分别为x和x’,这两个投影点之间存在什么关系呢?观察下图 

  • 点x、x’与摄像机中心C和C’是共面的,并且与空间点X也是空面的,这5个点共面于平面π!这是一个最本质的约束,即5个点决定了一个平面π
  • 由该约束,可以推导出一个重要性质:由图像点x和x’反投影的射线共面,并且,在平面π上,在搜索点对应中,该性质非常重要

3. 对极几何的几个相关概念

对极平面束(epipolar pencil):以基线为轴的平面束;下图给出了包含两个平面的对极平面束 

对极平面(epipolar plane):任何包含基线的平面都称为对极平面,或者说是对极平面束中的平面;例如,下图中的平面π就是一个对极平面 

对极点(epipole):摄像机的基线与每幅图像的交点;即上图中的点e和e’

对极线(epipolar line):对极平面与图像的交线;例如,上图中的直线l和l’

4. 对应点的约束

现在假设只知道图像点x,那么,它的对应点x’如何约束呢? 

根据前面的讨论,点x和x’一定位于平面π上,而平面π可以利用基线CC’和图像点x的反投影射线确定

点x’又是右侧图像平面上的点,所以,点x’一定位于平面ππ与右侧图像平面的交线l’上

前面提到,直线l’为点x的对极线,也就是说,点x的对应点x’一定位于它的对极线上!

重点来了,点x的对应点x’一定位于它的对极线上!这将是我们接下来工作的意义:


所谓极线约束就是说世界坐标系下的同一个点在两幅图像上的映射,已知左图映射点p1,那么右图映射点p2一定在相对于p1的极线上,这样可以减少待匹配的点数量。

对于极线约束方程可以由以下来表示:

三维向量x和x'存放相关点,F为一个3*3且秩为2的基础矩阵,那么:

这里我们引入opencv的函数computeCorrespondEpilines

如果已知F矩阵,左图特征点,作用是获取图像1中的二维特征点 在图像2中对应的外极线

或者如果已知F矩阵,右图特征点,获取图像2中的二维特征点 在图像1中对应的外极线

使用方法:

cv::computeCorrespondEpilines(cv::Mat(queryInliers), 1, F, lines1);

cv::computeCorrespondEpilines(cv::Mat(sceneInliers), 2, F, lines1);

这里F不用转置,因为源代码:

关于这个函数的计算公式:

内部实现看源代码:

cv::computeCorrespondEpilines(cv::Mat(sceneInliers), 2, F, lines1);

最后求出的线是三个float,即a,b,c

这里我们插个话题,很多论文也说了这么一个概念:

假设两条线都求出来了,对于两条直线,以连续点的方式存储:I和I‘分别在左右两幅图像上,若他们俩有对应关系,那么认为他们两条直线之间的点依次的存在对应关系,这种好像是极线匹配问题,假设初始的匹配点是绝对正确的,通过F矩阵,求出互相对应的极线,通过遍历在对应极线上的点,去寻找匹配点


代码:

代码中有

1 两种方式,把把Point2f 转换为KeyPoint 的方法

2 极线约束剔除算法 第二种实现,真正意义上的

等下,解释一个知识点:

求解Ax=b时候b不为零向量时候用cvsolve

在Ax=0时候是不能用cvsolve来接函数的,但是可以利用一下函数SVD::solveZ来求解。

A:

vec:

虽然上面我们没用到,但是知道多一点总是好的,上面是方程组的求解


图示效果:

利用若干初始匹配点对估计影像之间的基本矩阵F和单应矩阵H,从而实现基于对极几何和单应映射的双重约束(如上图),可同时提高匹配的速度和精度

#include <iostream>
#include <opencv2/opencv.hpp>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/xfeatures2d.hpp>
#define M_PI       3.14159265358979323846

using namespace cv;
using namespace std;

cv::Mat UpDownDrawInlier(const cv::Mat &queryImage, const cv::Mat &objectImage,
	const vector<cv::Point2f> &queryCoord, const vector<cv::Point2f> &objectCoord)
{
	Size sz = Size(queryImage.size().width,
		queryImage.size().height + objectImage.size().height);
	Mat matchingImage = Mat::zeros(sz, CV_8UC3);

	// 设置matchingImage的感兴趣区域大小并赋予原图
	Mat roi1 = Mat(matchingImage, Rect(0, 0, queryImage.size().width, objectImage.size().height));
	queryImage.copyTo(roi1);
	Mat roi2 = Mat(matchingImage, Rect(0, queryImage.size().height, objectImage.size().width, objectImage.size().height));
	objectImage.copyTo(roi2);

	//画出点
	for (int i = 0; i < (int)queryCoord.size(); ++i) {
		Point2f pt1 = queryCoord[i];
		Point2f pt2 = objectCoord[i];
		Point2f from = pt1;
		Point2f to = Point(pt2.x, queryImage.size().height + pt2.y);
		line(matchingImage, from, to, Scalar(0, 255, 255));
	}
	return matchingImage;
}

int main(int argc, const char * argv[]) {

	string imgFileName = "1.JPG";
	Mat queryImage;
	queryImage = imread(imgFileName);

	string objFileName = "2.JPG";
	Mat objectImage;
	objectImage = imread(objFileName);


	resize(queryImage, queryImage, Size(640, 480));
	resize(objectImage, objectImage, Size(640, 480));
	//imresize(queryImage, 480);
	//imresize(objectImage, 480);


	clock_t start, finish;
	start = clock();
	//检测SIFT特征点,方便后续做对比
	Ptr<xfeatures2d::SIFT>feature = xfeatures2d::SIFT::create(50);
	vector<KeyPoint> qKeypoints;
	feature->detect(queryImage, qKeypoints);
	Mat qDescriptor;
	feature->compute(queryImage, qKeypoints, qDescriptor);

	vector<KeyPoint> objKeypoints;
	feature->detect(objectImage, objKeypoints);
	Mat objDesriptor;
	feature->compute(objectImage, objKeypoints, objDesriptor);

	FlannBasedMatcher matcher;
	vector< DMatch > matches1;
	matcher.match(qDescriptor, objDesriptor, matches1);

	//点转换
	vector<cv::Point2f> queryCoord;
	vector<cv::Point2f> objectCoord;
	for (int i = 0; i < matches1.size(); i++){
		queryCoord.push_back((qKeypoints[matches1[i].queryIdx]).pt);
		objectCoord.push_back((objKeypoints[matches1[i].trainIdx]).pt);
	}

	// 计算homography矩阵
	vector<cv::Point2f> queryInliers;
	vector<cv::Point2f> sceneInliers;

	Mat mask;
	double minVal, maxVal;
	cv::minMaxIdx(queryCoord, &minVal, &maxVal);
	Mat F = findFundamentalMat(queryCoord, objectCoord, FM_RANSAC, 0.006 * maxVal, 0.99, mask); //threshold from [Snavely07 4.1]
	// N. Snavely, S. Seitz, R. Szeliski, “Modeling the World from Internet Photo Collections, ”Int’l J.of Computer Vision, Vol. 80, No. 2, pp.189–210, 2008.
	int inliers_cnt = 0, outliers_cnt = 0;
	for (int j = 0; j < mask.rows; j++){
		if (mask.at<uchar>(j) == 1){
			queryInliers.push_back(queryCoord[j]);
			sceneInliers.push_back(objectCoord[j]);
			inliers_cnt++;
		}
		else {
			outliers_cnt++;
		}
	}

	outliers_cnt = matches1.size() - inliers_cnt;
	Mat result = UpDownDrawInlier(queryImage, objectImage, queryInliers, sceneInliers);
	cout << "最近邻匹配特征点数  " << matches1.size() << endl;
	cout << "result内点数目  " << queryInliers.size() << endl;
	cout << "外点数目  " << outliers_cnt << endl;



	// ------------ 一种把Point2f  转换为  KeyPoint 的方法
	cv::Mat imagekeyPt1; // 左图
	cv::Mat imagekeyPt2; // 右图
	vector<KeyPoint> NewKeyP1;
	vector<KeyPoint> NewKeyP2;
	for (int i = 0; i < queryInliers.size(); i++){
		KeyPoint key;
		key.pt.x = queryInliers[i].x;
		key.pt.y = queryInliers[i].y;
		NewKeyP1.push_back(key);
		key.pt.x = sceneInliers[i].x;
		key.pt.y = sceneInliers[i].y;
		NewKeyP2.push_back(key);
	}
	cv::drawKeypoints(queryImage, NewKeyP1, imagekeyPt1, cv::Scalar(255, 0, 0), cv::DrawMatchesFlags::DEFAULT);
	cv::drawKeypoints(objectImage, NewKeyP2, imagekeyPt2, cv::Scalar(255, 0, 0), cv::DrawMatchesFlags::DEFAULT);


	// ------------ 第二种把Point2f  转换为  KeyPoint 的方法
	vector<KeyPoint> key1(queryInliers.size());
	vector<KeyPoint> key2(sceneInliers.size());
	KeyPoint::convert(queryInliers, key1);
	KeyPoint::convert(sceneInliers, key2);

	vector<DMatch> m_InlierMatches;
	m_InlierMatches.resize(queryInliers.size());
	int InlinerCount = 0;
	for (int i = 0; i<mask.rows; i++)
	{
		if (mask.at<uchar>(i) == 1)
		{
			m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
			m_InlierMatches[InlinerCount].trainIdx = InlinerCount;
			InlinerCount++;
		}
	}
	Mat img_matches1;
	drawMatches(queryImage, key1, objectImage, key2, m_InlierMatches, img_matches1);
	imshow("matches1", img_matches1);


	

	//--------------------  F 极线约束
	//--------------------  F 极线约束
	//--------------------  F 极线约束
	//--------------------  F 极线约束
	//--------------------  F 极线约束
	//--------------------  F 极线约束


	std::vector<cv::Vec3f> lines1; //存储外极线
	cv::computeCorrespondEpilines(cv::Mat(queryInliers), 1, F, lines1);//获取图像1中的二维特征点 在图像2中对应的外极线
	vector<cv::Point2f> srcInliers;
	vector<cv::Point2f> dstInliers;
	int num = 0;
	int num_count = 0;
	for (std::vector<cv::Vec3f>::const_iterator it1 = lines1.begin();
		it1 != lines1.end(); ++it1)
	{
		float a = (*it1)[0];// ax+by+c=0
		float b = (*it1)[1];
		float c = (*it1)[2];
		float x = sceneInliers[num].x;
		float y = sceneInliers[num].y;
		float z = a*x + b*y + c;
		float zz = abs(z);
		if (z<=2)
		{
			srcInliers.push_back(queryInliers[num]);
			dstInliers.push_back(sceneInliers[num]);
			num_count++;
		}
		num++;
	}

	Mat result1 = UpDownDrawInlier(queryImage, objectImage, srcInliers, dstInliers);
	cout << "result1内点数目  " << srcInliers.size() << endl;

	imshow("result1", result1);

	finish = clock();
	double sift_time = (double)(finish - start) / CLOCKS_PER_SEC;
	cout << setprecision(10) << fixed << "sift_time   " << sift_time << endl;


	//double inlier_ratio = (double)good_matches.size() / matches1.size();
	//cout << "匹配率  " << inlier_ratio << endl;
	imshow("matchingImage", result);
	waitKey();
	return 0;
}

或者

把x带入,求出y*,然后abs(y-y*)

两种方式效果一样

初始匹配点集中的内点(inliers)数量和分布对F和H的估计精度具有重要影响。如初始匹配点集中的外点(outliers)数量占优时,随机采样一致性(RANdom SAmple Consensus ,RANSAC)算法可能失效或估计偏差较大

存在一个问题:如果求出的H和F不对,剔除也会出错,所以很多论文提出把H和F计算的更好

比如论文:

基于 SIFT的宽基线立体影像最小二乘匹配方法
杨化超 ,张书毕, 张秋昭 

猜你喜欢

转载自blog.csdn.net/baidu_40840693/article/details/83187045