[再次續]關於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的寬基線立體影像最小二乘匹配方法
楊化超 ,張書畢, 張秋昭 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章