【OpenCV】基于SIFT/SURF算法的双目视差测距(一)

文章目录

  • 基本原理
  • 实现步骤
  • 程序效果
    • sift算法
    • surf算法
  • 代码

下篇链接: https://blog.csdn.net/qinchang1/article/details/88412058

基本原理

由于本文程序直接使用opencv库,因此对于SIFT和SURF算法的原理不做详细介绍。
简单来说,该两种算法使用特征向量来描述特征点,我们可以通过计算特征向量的距离来匹配两张图中对应的特征点。值得注意的是,SIFT由于计算方法不同,且维度较高,计算速度上较SURF更慢。

下面简单介绍一下双目测距原理,这也是本次实验的关键。
下图是双目摄像头成像示意图,其中P点为目标物体,O1和O2分别为左右摄像头的投影中心,x1和x2分别是目标物体在左右摄像头成像平面上的坐标,f为焦距,T为左右摄像头中心线距离,Z为摄像头投影中心到目标物体的距离。
【OpenCV】基于SIFT/SURF算法的双目视差测距(一)_第1张图片
通过计算相似三角形我们可以得到:
在这里插入图片描述
其中,
1.(x1-x2)实际上就是视差,单位是实际物理量mm,用D来表示这个视差,并将单位换成像素,多出来的常数放到分子中;
2.分母中的fT是一个常数,我们用K来表示,这里面还可以包括像素单位到mm单位的比例系数。
整理一下可以得到:
在这里插入图片描述.
其中,
1.?(mm)为摄像头投影中心到物体的距离;
2.?(像素)为视差,与?1−?2 成比例关系;
3.?(mm*像素)为常数。
*从公式可以看出,距离Z视差D是成反比例关系的。因此,我们只要得到反比例系数K,就可以通过视差值来计算距离值。
【OpenCV】基于SIFT/SURF算法的双目视差测距(一)_第2张图片
由于双目测距中视差和距离成反比例关系,从上图可以看出:
1.在距离较近的地方,较小的距离变化会引起较大的视差变化;
2.在距离较远的地方,较大的距离变化仅引起较小的视差变化。
因此,双目视差测距有一定的局限性,即在较远或较近的地方效果不会很好。

实现步骤

由于博主假期在家,未带回双目摄像头,因此本次实验使用手机拍摄的照片模拟左右图像,仅实现视差的获取(完整的双目测距下篇文章再续)。
主要步骤:
1、导入左右图像;
2、使用SiftFeatureDetectorSurfFeatureDetector检测关键点(ps:此处为opencv2.4.13版本的检测器,3.0版本之后不太一样);
3、使用SiftDescriptorExtractorSurfDescriptorExtractor提取各个关键点的描述(特征向量);
4、使用FlannBasedMatcher匹配器进行两图像的关键点匹配(这里面会包含很多错误匹配);
5、计算出所有匹配中的最小distance,筛选出小于两倍最小匹配距离的匹配作为正确的匹配;
6、计算出正确匹配的两特征点的X坐标差,即为该匹配点的像素视差;
7、去除掉偏差值较大的视差,计算视差均值作为最后的结果。

程序效果

输入的左右图像:
【OpenCV】基于SIFT/SURF算法的双目视差测距(一)_第3张图片

sift算法

匹配效果:
【OpenCV】基于SIFT/SURF算法的双目视差测距(一)_第4张图片
匹配点视差数据(显示不全):
【OpenCV】基于SIFT/SURF算法的双目视差测距(一)_第5张图片
筛选后视差结果:
【OpenCV】基于SIFT/SURF算法的双目视差测距(一)_第6张图片

surf算法

匹配效果:
【OpenCV】基于SIFT/SURF算法的双目视差测距(一)_第7张图片
匹配点视差数据:
【OpenCV】基于SIFT/SURF算法的双目视差测距(一)_第8张图片
筛选后视差结果:
【OpenCV】基于SIFT/SURF算法的双目视差测距(一)_第9张图片
ps:两种方法最后计算出来的视差结果差别不大,但是SIFT算法的速度明显慢于SURF很多,不过前者获得的匹配点数量也更多。

代码

#include   
#include   
#include 
#include 
#include 
#include  
#include 
#include 

using namespace cv;
using namespace std;

class parallax
{
public:
	double leftX;
	double rightX;
	double paraValue;
};

bool ascendPara(parallax a, parallax b)
{
	return a.paraValue < b.paraValue;
}

int main()
{
	Mat leftImg, rightImg;
	leftImg = imread("limg.jpg");
	rightImg = imread("rimg.jpg");
	imshow("limg", leftImg);
	imshow("rimg", rightImg);

	int minhessian = 1000;//threshold of hessian in SIFT or SURF algorithm 
	vector<KeyPoint>l_keyPoint, r_keyPoint;
	Mat l_descriptor, r_descriptor;

	SurfFeatureDetector detector(minhessian);//define a feature detection class object
	detector.detect(leftImg, l_keyPoint);
	detector.detect(rightImg, r_keyPoint);

	//compute descriptor (feature vector) of key points
	SurfDescriptorExtractor extractor;
	extractor.compute(leftImg, l_keyPoint, l_descriptor);
	extractor.compute(rightImg, r_keyPoint, r_descriptor);

	//FLANN algorithm to match feature vector
	FlannBasedMatcher matcher;
	vector<DMatch>matches;
	matcher.match(l_descriptor, r_descriptor, matches);

	//calculate the max and min distance between key points
	double maxdist = 0; double mindist = 100;
	for (int i = 0; i < l_descriptor.rows; i++)
	{
		double dist = matches[i].distance;
		if (dist < mindist)mindist = dist;
		if (dist > maxdist)maxdist = dist;
	}
	cout << "Matching quantity:" << matches.size() << endl;

	//select the good match points
	vector<DMatch>goodMatches;
	for (int i = 0; i < l_descriptor.rows; i++)
	{
		if (matches[i].distance<2 * mindist)
		{
			goodMatches.push_back(matches[i]);
		}
	}
	cout << "Good matching quantity:" << goodMatches.size() << endl;

	//calculate parallax
	vector<parallax>para;
	for (int i = 0; i < goodMatches.size(); i++)
	{
		parallax temp;
		temp.leftX = l_keyPoint[goodMatches[i].queryIdx].pt.x;
		temp.rightX = r_keyPoint[goodMatches[i].trainIdx].pt.x;
		temp.paraValue = temp.leftX - temp.rightX;
		para.push_back(temp);
		cout << "No." << i + 1 << ":\t l_X ";
		cout << para[i].leftX << "\t r_X " << para[i].rightX;
		cout << "\t parallax " << para[i].paraValue << endl;
	}
	sort(para.begin(), para.end(), ascendPara);
	int idxMedian = int(para.size()/2);
	double paraMedian = para[idxMedian].paraValue;
	vector<parallax>::iterator it;
	double errorRange = 0.005;
	for (it=para.begin(); it!=para.end(); )
	{
		if (it->paraValue<((1 - errorRange)*paraMedian) || it->paraValue>((1 + errorRange)*paraMedian))
			it = para.erase(it);
		else
			it++;
	}
	cout << "Final data..." << endl;
	double paraSum = 0;
	double paraMean;
	for (int i = 0; i < para.size(); i++)
	{
		paraSum = paraSum + para[i].paraValue;
		cout << "No." << i << "\t" << para[i].paraValue << endl;
	}
	paraMean = paraSum / para.size();
	cout << "Parallax is " << paraMean << " pixel." << endl;

	//draw the match image
	Mat matchImg;
	drawMatches(leftImg, l_keyPoint, rightImg, r_keyPoint, goodMatches, matchImg,
		Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
	imshow("match", matchImg);

	waitKey(0);
	return 0;
}

ps:1.代码中使用的是SURF算法,需要用SIFT的只需要将代码中SurfFeatureDetectorSurfDescriptorExtractor改成SiftFeatureDetectorSiftDescriptorExtractor即可;
2.匹配结果DMatch中存储着匹配点对的索引,分别在DMatch[i].queryIdxDMatch[i].trainIdx中,这是找到匹配对的关键。

本次仅实现了像素视差的获取,下篇待博主回校后使用双目摄像头来试验实际测距效果。
下篇链接:https://blog.csdn.net/qinchang1/article/details/88412058

如有错误,欢迎指正!

你可能感兴趣的:(OpenCV)