Shi-Tomas檢測和SHIF角點匹配

⁽⁽ƪ(•̩̩̩̩_•̩̩̩̩)ʃ⁾⁾ᵒ發表於2020-11-27

Shi-Tomas角點檢測與SFIT匹配

Harris 角點檢測是一種最為常見的方法,但其運算量大,計算速度慢,因此不怎麼受寵,Shi-Tomas角點檢測執行速度快,且其原理簡單,SFIT匹配適用於旋轉、尺度變換,且對視覺變化、放射變換、噪聲也保持一定程度的穩定性,最重要的是能與其他形式的特徵向量進行聯合。

本文創新點:將Shi-Tomas角點檢測於SFIT匹配結合使用,且進行了亞畫素檢測,通過多種方法的比較和驗證得出這兩種方法結合使用效果均優於其他方法。

1. 常用的角點檢測方法:Moravec角點檢測、FAST角點檢測、Harris角點檢測和Shi-Tomas角點檢測。
2. 影像的匹配分為特徵點匹配和灰度匹配。
3. 角點匹配可以分為以下四個步驟:

1.提取檢測子:在兩張待匹配的影像中尋找那些最容易識別的畫素點(角點),比如紋理豐富的物體邊緣點等。

2.提取描述子:對於檢測出的角點,用一些數學上的特徵對其進行描述,如梯度直方圖,區域性隨機二值特徵等。

​ 檢測子和描述子的常用提取方法有:SIFT, Harris, Surf, Fast, Agast, Brisk, Freak, Brisk,Orb等。

3.匹配:通過各個角點的描述子來判斷它們在兩張影像中的對應關係。常用方法如FLANN.

4.去外點:去除錯誤匹配的外點,保留正確的內點。常用方法有Ransac, GTM。

4. 部分原始碼

程式碼原創,且已封裝為函式,可直接呼叫

void  ImageFeatureMatch(Mat &Image1, Mat &Image2, vector<CVertex4>& Vertexes, Mat &img_matches)
{
	vector<DMatch> matches;
	float Error=50;
	Mat image1_Corner_graysrcImage, image2_Corner_graysrcImage;
	vector<Point2f> image1_Corners, image2_Corners;
	vector<KeyPoint> image1_Keypoints, image2_Keypoints;
	double qualityLevel = 0.01; //角點檢測可接受的最小特徵值
	int blockSize = 3; //計算導數自相關矩陣時指定的鄰域範圍
	double k = 0.04; //權重係數
	int G_maxCornerNumber = 1000,
		MaxTrackbar_minDistance = 20,
		G_maxTrackbar_CornerNumber = 1000,
		MinDistance = 1;

	cvtColor(Image1,image1_Corner_graysrcImage,  COLOR_BGR2GRAY);//影像灰度化處理
	cvtColor(Image2,image2_Corner_graysrcImage,  COLOR_BGR2GRAY);
	
	goodFeaturesToTrack(image1_Corner_graysrcImage,//輸入影像
		image1_Corners,//檢測到的角點的輸出向量
		G_maxCornerNumber,//角點的最大數量
		qualityLevel,//角點檢測可接受的最小特徵值
		MinDistance,//角點之間的最小距離
		Mat(),//感興趣區域
		blockSize,//計算導數自相關矩陣時指定的鄰域範圍
		false,//不使用Harris角點檢測
		k);//權重係數
		
	goodFeaturesToTrack(image2_Corner_graysrcImage, image2_Corners, G_maxCornerNumber, qualityLevel,
		MinDistance, Mat(), blockSize, false, k);

	//進行亞畫素檢測
	TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
	cornerSubPix(image1_Corner_graysrcImage, image1_Corners, Size(5, 5), Size(-1, -1), criteria);
	cornerSubPix(image2_Corner_graysrcImage, image2_Corners, Size(5, 5), Size(-1, -1), criteria);

	//將Point2f轉化為KeyPoint型別
	KeyPoint::convert(image1_Corners, image1_Keypoints, 1, 1, 0, -1);
	KeyPoint::convert(image2_Corners, image2_Keypoints, 1, 1, 0, -1);

	//Sift角點匹配
	cv::Ptr<cv::SIFT > detector = cv::SIFT::create();
	Mat image1_Descriptors, image2_Descriptors;
	detector->detectAndCompute(image1, Mat(), image1_Keypoints, image1_Descriptors, 1);
	detector->detectAndCompute(image2, Mat(), image2_Keypoints, image2_Descriptors, 1);

	//匹配,計算單應性矩陣H,用RANSCA剔除誤匹配點
	FlannBasedMatcher matcher;
	matcher.match(image1_Descriptors, image2_Descriptors, matches);
	vector<Point2f> image1_Matches, image2_Matches;
	int Match_Points_Num = matches.size();
	for (int i = 0; i < Match_Points_Num; i++)//將得到的左右匹配結果點分別存入動態陣列
	{
		image1_Matches.push_back(image1_Keypoints[matches[i].queryIdx].pt);
		image2_Matches.push_back(image2_Keypoints[matches[i].trainIdx].pt);
	}
	Mat H = findHomography(image1_Matches, image2_Matches, RANSAC);//計算單應性矩陣並返回,RANSAC演算法去除誤差匹配
	vector<char> mask(image1_Matches.size());
	
	vector<Point2f> image1_Matches_Aftertrans;
	perspectiveTransform(image1_Matches, image1_Matches_Aftertrans, H);//進行透射變換
	for (int i = 0; i < image2_Matches.size(); i++)
	{
		if (norm(image2_Matches[i] - image1_Matches_Aftertrans[i]) <= Error)//得到配對的關鍵點描述子
		{
			mask[i] = 1;
		}
	}
	drawMatches(image1, image1_Keypoints, image2, image2_Keypoints, matches, img_matches, Scalar::all(-1), Scalar::all(-1), mask, DrawMatchesFlags::DEFAULT);
	imshow("匹配結果", img_matches);
	

5. 部分程式碼詳解

①函式共四個引數,引數1 2 分別為輸入的兩個待匹配影像,引數3定義一個CVertex4自定義型別的vector,用於存放匹配後各角點的座標(可用於標記或者深度資訊的計算),引數4位匹配後且標記了角點的影像。

定義結構體
struct CVertex4
{
	double x1, y1;
	double x2, y2;
};

②goodFeaturesToTrack()函式用於角點檢測

goodFeaturesToTrack(image1_Corner_graysrcImage,//輸入影像
		image1_Corners,//檢測到的角點的輸出向量
		G_maxCornerNumber,//角點的最大數量
		qualityLevel,//角點檢測可接受的最小特徵值
		MinDistance,//角點之間的最小距離
		Mat(),//感興趣區域  
		blockSize,//計算導數自相關矩陣時指定的鄰域範圍
		false,//不使用Harris角點檢測
		k);//權重係數

注:此處引數6 Mat(),如果無感興趣區域也不可以致為NULL。如果在除錯程式碼過程中發現如下圖所示的錯誤,可以排除此處。(此類錯誤不只是影像或者視訊儲存路徑的問題)。

在這裡插入圖片描述

③cv::TermCriteria類

定義迭代演算法終止條件的類, 可以通過預設建構函式對其進行初始化,然後覆蓋任何引數,或者可以使用帶引數的建構函式對結構進行完全初始化。 

預設構造和有參構造 :
cv::TermCriteria::TermCriteria()    

cv::TermCriteria::TermCriteria(
        int     type,//終止條件的型別,TermCriteria::Type之一。
        int     maxCount,//要計算的最大迭代次數或元素。
        double  epsilon //迭代演算法停止的期望精度或引數更改。
)     

eg:

TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);

​ ④cv::cornerSubPix()亞畫素角點檢測

​ cv::goodFeaturesToTrack()提取到的角點只能達到畫素級別,在很多情況下並不能滿足實際的需求,這時,我們則需要使用cv::cornerSubPix()對檢測到的角點作進一步的優化計算,可使角點的精度達到亞畫素級別。

void cv::cornerSubPix(
	cv::InputArray image, // 輸入影像
	cv::InputOutputArray corners, // 角點(既作為輸入也作為輸出)
	cv::Size winSize, // 區域大小為 NXN; N=(winSize*2+1)
	cv::Size zeroZone, // 類似於winSize,但是總具有較小的範圍,Size(-1,-1)表示忽略
	cv::TermCriteria criteria // 停止優化的標準
);

eg:

cornerSubPix(image1_Corner_graysrcImage, image1_Corners, Size(5, 5), Size(-1, -1), criteria); cornerSubPix(image2_Corner_graysrcImage, image2_Corners, Size(5, 5), Size(-1, -1), criteria);

⑤match()從查詢集中查詢EG每個描述符的最佳匹配

void match(
    InputArray queryDescriptors, //查詢描述符集
	InputArray trainDescriptors, //訓練描述符集合
	CV_OUT std::vector& matches, //匹配
	InputArray mask=noArray() //指定輸入查詢和描述符的列表矩陣之間的允許匹配的掩碼
) const;

⑥FlannBasedMatcher
FLANN的含義是Fast Library forApproximate Nearest Neighbors,從字面意思可知它是一種近似法,演算法更快但是找到的是最近鄰近似匹配,所以當我們需要找到一個相對好的匹配但是不需要最佳匹配的時候往往使用FlannBasedMatcher。當然也可以通過調整FlannBasedMatcher的引數來提高匹配的精度或者提高演算法速度,但是相應地演算法速度或者演算法精度會受到影響。

⑦findHomography()函式

Mat cv::findHomography	(	InputArray 	srcPoints,
                                InputArray 	dstPoints,
                                int 	method = 0,
                                double 	ransacReprojThreshold = 3,
                                OutputArray 	mask = noArray(),
                                const int 	maxIters = 2000,
                                const double 	confidence = 0.995 
)

引數:

srcPoints : 源平面中點的座標矩陣,可以是CV_32FC2型別,也可以是vector型別

dstPoints : 目標平面中點的座標矩陣,可以是CV_32FC2型別,也可以是vector型別

method : 計算單應矩陣所使用的方法。不同的方法對應不同的引數,具體如下:

​ 0—利用常規方法

​ RANSAC—基於RANSAC的魯棒性

​ LMEDS—最小中值魯棒演算法

​ RHO----基於PROSAC的魯棒演算法

ransacReprojThreshold : 將點對視為內點的最大允許重投影錯誤閾值(僅用於RANSAC和RHO方法)。

mask : 可選輸出掩碼矩陣,通常由魯棒演算法(RANSAC或LMEDS)設定。 請注意,輸入掩碼矩陣是不需要設定的。

maxIters : RANSAC演算法的最大迭代次數,預設值為2000。

confidence : 可信度值,取值範圍為0到1。

該函式能夠找到並返回源平面和目標平面之間的轉換矩陣H,使得反向投影錯誤率達到最小。

如果方法引數的值為預設值0,則該函式使用所有點對來計算具有簡單最小二乘的初始單應性估計。

但是,如果不是所有的點對(srcPointsi,dstPointsi)都適合剛性透視變換(也就是說,存在一些外點(匹配錯誤點對)),那麼這個初始估計就會很差。 在這種情況下,可以使用三種魯棒方法之一。 RANSAC,LMeDS和RHO等演算法嘗試對應點對(每對四對)的許多不同隨機子集,使用該子集和簡單最小二乘演算法估計單應矩陣,然後計算單應性的效能(對於RANSAC來說,單應性的質量是指內點的數量,對於LMeDs來說,單應性的質量是指中值重投影的誤差)。 最後,使用最佳子集來產生單應矩陣的初始估計和內點/異常值的掩模。

RANSAC和RHO方法幾乎可以處理任何外點比例,但需要一個閾值來區分內點和外點。 LMeDS演算法不需要任何閾值,但只有在超過50%的內點時它才能正常工作。 最後,如果沒有外點且噪聲很小,可以使用預設方法(method = 0)。

⑧prespectiveTranstorm()函式

	對二維或者三維向量進行透射變換,也就是對輸入二維座標點或者三維座標點進行透射變換。或者對輸入的影像進行透射變換

void perspectiveTransform(InputArray src, OutputArray dst, InputArray m);

注意1.這裡src和dst的輸入並不是影像,而是影像對應的座標。

​ 2.這種將原圖變換到對應影像上的方式會有一些沒有被填充的點,也就是右圖中黑色的小點。解決這種問題一是用差值的方式,再一種比較簡單就是不用原圖的點變換後對應找新圖的座標,而是直接在新圖上找反向變換原圖的點。

⑨ 仿射變換和透射變換又稱平面變換(二維座標變換)和空間變換(三維座標變換)
在這裡插入圖片描述

仿射變換能夠保持影像的“平直性”,包括旋轉,縮放,平移,錯切操作。一般而言,仿射變換矩陣為2*3的矩陣,第三列的元素起著平移的作用,前面兩列的數字對角線上是縮放,其餘為旋轉或者錯切的作用。
仿射變換是透視變換的特例

在這裡插入圖片描述

透視變換能保持“直線性”,即原影像裡面的直線,經透視變換後仍為直線。

​ 從另一個角度也能說明三維變換和二維變換的意思,仿射變換的方程組有6個未知數,所以要求解就需要找到3組對映點,三個點剛好確定一個平面。透視變換的方程組有8個未知數,所以要求解就需要找到4組對映點,四個點就剛好確定了一個三維空間。
仿射變換和透視變換其計算方法為座標向量和變換矩陣的乘積,換言之就是矩陣運算。在應用層面,放射變換是影像基於3個固定頂點的變換, 透視變換是影像基於4個固定頂點的變換。

void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

​ ⑩ 影像掩膜處理的作用

​ 1.1 提取感興趣區:用預先製作的感興趣區掩膜與待處理影像相乘,得到感興趣區影像,感興趣區內影像值保持不變,而區外影像值都為0;
​ 1.2 遮蔽作用:用掩膜對影像上某些區域作遮蔽,使其不參加處理或不參加處理引數的計算,或僅對遮蔽區作處理或統計;
​ 1.3 結構特徵提取:用相似性變數或影像匹配方法檢測和提取影像中與掩膜相似的結構特徵;
​ 1.4 特殊形狀影像的製作。

相關文章