1. 程式人生 > >學習OpenCV範例(二十一)——Keypoints+Knn+findHomography進行目標定位

學習OpenCV範例(二十一)——Keypoints+Knn+findHomography進行目標定位

本範例的程式碼主要都是 學習OpenCV——通過KeyPoints進行目標定位這篇部落格提供的,然後在它的基礎上稍加修改,檢測keypoints點的檢測器是SURF,獲取描述子也是用到SURF來描述,而用到的匹配器是FlannBased,匹配的方式是Knn方式,最後通過findHomography尋找單對映矩陣,perspectiveTransform獲得最終的目標,在這個過程中還通過單對映矩陣來進一步去除偽匹配,這裡只是貼出程式碼和程式碼解析,至於原理還沒弄得特別明白,希望接下來可以繼續學習,學懂了演算法原理再來補充。

1、程式碼實現

#include "stdafx.h"
#include "opencv2/opencv.hpp"
#include <vector>  
#include <iostream>  

using namespace cv;  
using namespace std;  
Mat src,frameImg;  
int width;  
int height;  
vector<Point> srcCorner(4);  
vector<Point> dstCorner(4);  

static bool createDetectorDescriptorMatcher( const string& detectorType, const string& descriptorType, const string& matcherType,  
	Ptr<FeatureDetector>& featureDetector,  
	Ptr<DescriptorExtractor>& descriptorExtractor,  
	Ptr<DescriptorMatcher>& descriptorMatcher )  
{  
	cout << "< Creating feature detector, descriptor extractor and descriptor matcher ..." << endl;  
	if (detectorType=="SIFT"||detectorType=="SURF")  
		initModule_nonfree();  
	featureDetector = FeatureDetector::create( detectorType );  
	descriptorExtractor = DescriptorExtractor::create( descriptorType );  
	descriptorMatcher = DescriptorMatcher::create( matcherType );  
	cout << ">" << endl;  
	bool isCreated = !( featureDetector.empty() || descriptorExtractor.empty() || descriptorMatcher.empty() );  
	if( !isCreated )  
		cout << "Can not create feature detector or descriptor extractor or descriptor matcher of given types." << endl << ">" << endl;  
	return isCreated;  
}  


bool refineMatchesWithHomography(const std::vector<cv::KeyPoint>& queryKeypoints,    
	const std::vector<cv::KeyPoint>& trainKeypoints,     
	float reprojectionThreshold,    
	std::vector<cv::DMatch>& matches,    
	cv::Mat& homography  )  
{  
	const int minNumberMatchesAllowed = 4;    
	if (matches.size() < minNumberMatchesAllowed)    
		return false;    
	// Prepare data for cv::findHomography    
	std::vector<cv::Point2f> queryPoints(matches.size());    
	std::vector<cv::Point2f> trainPoints(matches.size());    
	for (size_t i = 0; i < matches.size(); i++)    
	{    
		queryPoints[i] = queryKeypoints[matches[i].queryIdx].pt;    
		trainPoints[i] = trainKeypoints[matches[i].trainIdx].pt;    
	}    
	// Find homography matrix and get inliers mask    
	std::vector<unsigned char> inliersMask(matches.size());    
	homography = cv::findHomography(queryPoints,     
		trainPoints,     
		CV_FM_RANSAC,     
		reprojectionThreshold,     
		inliersMask);    
	std::vector<cv::DMatch> inliers;    
	for (size_t i=0; i<inliersMask.size(); i++)    
	{    
		if (inliersMask[i])    
			inliers.push_back(matches[i]);    
	}    
	matches.swap(inliers);  
	Mat homoShow;  
	drawMatches(src,queryKeypoints,frameImg,trainKeypoints,matches,homoShow,Scalar::all(-1),CV_RGB(255,255,255),Mat(),2);       
	imshow("homoShow",homoShow);   
	return matches.size() > minNumberMatchesAllowed;   

}  


bool matchingDescriptor(const vector<KeyPoint>& queryKeyPoints,const vector<KeyPoint>& trainKeyPoints,  
	const Mat& queryDescriptors,const Mat& trainDescriptors,   
	Ptr<DescriptorMatcher>& descriptorMatcher,  
	bool enableRatioTest = true)  
{  
	vector<vector<DMatch>> m_knnMatches;  
	vector<DMatch>m_Matches;  

	if (enableRatioTest)  
	{  
		cout<<"KNN Matching"<<endl;  
		const float minRatio = 1.f / 1.5f;  
		descriptorMatcher->knnMatch(queryDescriptors,trainDescriptors,m_knnMatches,2);  
		for (size_t i=0; i<m_knnMatches.size(); i++)  
		{  
			const cv::DMatch& bestMatch = m_knnMatches[i][0];  
			const cv::DMatch& betterMatch = m_knnMatches[i][1];  
			float distanceRatio = bestMatch.distance / betterMatch.distance;  
			if (distanceRatio < minRatio)  
			{  
				m_Matches.push_back(bestMatch);  
			}  
		}  

	}  
	else  
	{  
		cout<<"Cross-Check"<<endl;  
		Ptr<cv::DescriptorMatcher> BFMatcher(new cv::BFMatcher(cv::NORM_HAMMING, true));  
		BFMatcher->match(queryDescriptors,trainDescriptors, m_Matches );  
	}  
	Mat homo;  
	float homographyReprojectionThreshold = 1.0;  
	bool homographyFound = refineMatchesWithHomography(  
		queryKeyPoints,trainKeyPoints,homographyReprojectionThreshold,m_Matches,homo);  

	if (!homographyFound)  
		return false;  
	else  
	{  
		if (m_Matches.size()>10)
		{
			std::vector<Point2f> obj_corners(4);
			obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( src.cols, 0 );
			obj_corners[2] = cvPoint( src.cols, src.rows ); obj_corners[3] = cvPoint( 0, src.rows );
			std::vector<Point2f> scene_corners(4);
			perspectiveTransform( obj_corners, scene_corners, homo);
			line(frameImg,scene_corners[0],scene_corners[1],CV_RGB(255,0,0),2);  
			line(frameImg,scene_corners[1],scene_corners[2],CV_RGB(255,0,0),2);  
			line(frameImg,scene_corners[2],scene_corners[3],CV_RGB(255,0,0),2);  
			line(frameImg,scene_corners[3],scene_corners[0],CV_RGB(255,0,0),2);  
			return true;  
		}
		return true;
	}  


}  
int main()  
{  
	string filename = "box.png";  
	src = imread(filename,0);  
	width = src.cols;  
	height = src.rows;  
	string detectorType = "SIFT";  
	string descriptorType = "SIFT";  
	string matcherType = "FlannBased";  

	Ptr<FeatureDetector> featureDetector;  
	Ptr<DescriptorExtractor> descriptorExtractor;  
	Ptr<DescriptorMatcher> descriptorMatcher;  
	if( !createDetectorDescriptorMatcher( detectorType, descriptorType, matcherType, featureDetector, descriptorExtractor, descriptorMatcher ) )  
	{  
		cout<<"Creat Detector Descriptor Matcher False!"<<endl;  
		return -1;  
	}  
	//Intial: read the pattern img keyPoint  
	vector<KeyPoint> queryKeypoints;  
	Mat queryDescriptor;  
	featureDetector->detect(src,queryKeypoints);  
	descriptorExtractor->compute(src,queryKeypoints,queryDescriptor);  

	VideoCapture cap(0); // open the default camera  
	cap.set( CV_CAP_PROP_FRAME_WIDTH,320);
	cap.set( CV_CAP_PROP_FRAME_HEIGHT,240 );
	if(!cap.isOpened())  // check if we succeeded  
	{  
		cout<<"Can't Open Camera!"<<endl;  
		return -1;  
	}  
	srcCorner[0] = Point(0,0);  
	srcCorner[1] = Point(width,0);  
	srcCorner[2] = Point(width,height);  
	srcCorner[3] = Point(0,height);  

	vector<KeyPoint> trainKeypoints;  
	Mat trainDescriptor;  

	Mat frame,grayFrame;
	char key=0;  

	//	frame = imread("box_in_scene.png");  
	while (key!=27)  
	{  
		cap>>frame;  
		if (!frame.empty())
		{
			frame.copyTo(frameImg);
			printf("%d,%d\n",frame.depth(),frame.channels());
			grayFrame.zeros(frame.rows,frame.cols,CV_8UC1);
			cvtColor(frame,grayFrame,CV_BGR2GRAY);  
			trainKeypoints.clear();  
			trainDescriptor.setTo(0);  
			featureDetector->detect(grayFrame,trainKeypoints);  

			if(trainKeypoints.size()!=0)  
			{  
				descriptorExtractor->compute(grayFrame,trainKeypoints,trainDescriptor);  

				bool isFound = matchingDescriptor(queryKeypoints,trainKeypoints,queryDescriptor,trainDescriptor,descriptorMatcher);  
				imshow("foundImg",frameImg);  

			}  
		}
		key = waitKey(1); 	
	}  
	cap.release();
	return 0;
}  

2、執行結果


3、用到的類和函式

DescriptorMatcher::knnMatch

功能:對查詢集的每個描述子尋找k個最好的匹配子

結構:

 void DescriptorMatcher::knnMatch(const Mat& queryDescriptors, const Mat& trainDescriptors, vector<vector<DMatch>>& matches, int k, const Mat& mask=Mat(), bool compactResult=false ) const

queryDescriptors :查詢描述子集
trainDescriptors :訓練描述子集
mask :掩碼指定查詢和訓練描述子集中哪些允許被匹配
matches :每一個matches[i]都有k個或少於k個的匹配子
k :每個查詢描述子最好的匹配子數量,如果查詢描述子的總數量少於k,則取總數量
compactResult :當masks不為空時,使用此引數,如果compactResult為false,那麼matches的容量和查詢描述子的數量一樣多,如果為true,則matches的容量就沒有包含在mask中剔除的匹配點

BFMatcher::BFMatcher

功能:蠻力匹配

結構:

 BFMatcher::BFMatcher(int normType=NORM_L2, bool crossCheck=false )

normType :NORM_L1, NORM_L2, NORM_HAMMING, NORM_HAMMING2四個中的一個. L1 和L2 規範 對於SIFT和SURF是個更好的選擇,而 NORM_HAMMING 應該被用在ORB, BRISK和 BRIEF中, NORM_HAMMING2被用在 ORB 中,當 WTA_K==3 或4 的時候,(詳見ORB::ORB描述子)

crossCheck :如果為false,則預設為BFMatcher行為,對每個查詢描述子中尋找k個最接近的鄰居,如果為true,則是knnMatch() 方法,而k=1,返回一對匹配子,這個方法只返回最接近的距離的一對匹配子,當有足夠多的匹配子的時候,這種方法通常能夠產生最好的結果和最小的誤差。

findHomography

功能:在兩個平面之間尋找單對映變換矩陣

結構:

Mat findHomography(InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray() )

srcPoints :在原平面上點的座標,CV_32FC2 的矩陣或者vector<Point2f>
dstPoints :在目標平面上點的座標,CV_32FC2 的矩陣或者 vector<Point2f> .
method –
用於計算單對映矩陣的方法. 
0 - 使用所有的點的常規方法
CV_RANSAC - 基於 RANSAC 的方法

CV_LMEDS - 基於Least-Median 的方法

ransacReprojThreshold:處理一組點對為內部點的最大容忍重投影誤差(只在RANSAC方法中使用),其形式為:    如果     \| \texttt{dstPoints} _i -  \texttt{convertPointsHomogeneous} ( \texttt{H} * \texttt{srcPoints} _i) \|  >  \texttt{ransacReprojThreshold}

那麼點i則被考慮為內部點,如果srcPoints和dstPoints是以畫素為單位,通常把引數設定為1-10範圍內 

mask :可選的輸出掩碼( CV_RANSAC or CV_LMEDS ). 輸入的掩碼值被忽略. (儲存inliers的點)                            

這個函式的作用是在原平面和目標平面之間返回一個單對映矩陣

s_i  \vecthree{x'_i}{y'_i}{1} \sim H  \vecthree{x_i}{y_i}{1}

因此反投影誤差\sum _i \left ( x'_i- \frac{h_{11} x_i + h_{12} y_i + h_{13}}{h_{31} x_i + h_{32} y_i + h_{33}} \right )^2+ \left ( y'_i- \frac{h_{21} x_i + h_{22} y_i + h_{23}}{h_{31} x_i + h_{32} y_i + h_{33}} \right )^2是最小的。

如果引數被設定為0,那麼這個函式使用所有的點和一個簡單的最小二乘演算法來計算最初的單應性估計,但是,如果不是所有的點對都完全符合透視變換,那麼這個初始的估計會很差,在這種情況下,你可以使用兩個robust演算法中的一個。 RANSAC 和LMeDS , 使用座標點對生成了很多不同的隨機組合子集(每四對一組),使用這些子集和一個簡單的最小二乘法來估計變換矩陣,然後計算出單應性的質量,最好的子集被用來產生初始單應性的估計和掩碼。
RANSAC方法幾乎可以處理任何異常,但是需要一個閾值, LMeDS 方法不需要任何閾值,但是隻有在inliers大於50%時才能計算正確,最後,如果沒有outliers和噪音非常小,則可以使用預設的方法。

PerspectiveTransform

功能:向量陣列的透視變換

結構:

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

src :輸入兩通道或三通道的浮點陣列,每一個元素是一個2D/3D 的向量轉換

dst :輸出和src同樣的size和type
m :3x3 或者4x4浮點轉換矩陣
轉換方法為:

(x, y, z)  \rightarrow (x'/w, y'/w, z'/w)

(x', y', z', w') =  \texttt{mat} \cdot \begin{bmatrix} x & y & z & 1  \end{bmatrix}

w =  \fork{w'}{if $w' \ne 0$}{\infty}{otherwise}

相關推薦

學習OpenCV範例——Keypoints+Knn+findHomography進行目標定位

本範例的程式碼主要都是 學習OpenCV——通過KeyPoints進行目標定位這篇部落格提供的,然後在它的基礎上稍加修改,檢測keypoints點的檢測器是SURF,獲取描述子也是用到SURF來描述,而用到的匹配器是FlannBased,匹配的方式是Knn方式,最後通過f

Python小白學習之路—【迭代器】

迭代器 1.迭代器協議 物件必須提供一個 next 方法,執行該方法要麼返回迭代中的下一項,要麼就引起一個Stoplteration異常,以終止迭代(只能往後走不能往前退) 2.可迭代物件 實現了迭代器協議的物件(如何實現:物件內部定義一個_iter_()方法) 協議是一種約定,可迭代物件實現了

Python學習之旅

Python基礎知識(20):錯誤、除錯和測試 一、錯誤處理 在執行程式的過程中有可能會出錯,一般我們會在新增一段程式碼在可能出錯的地方,返回約定的值,就可以知道會不會出錯以及出錯的原因 1、使用try......except......finally......錯誤處理機制 try...可能會出異常

【零基礎】Python3學習課後練習題

本文是跟著魚C論壇小甲魚零基礎學習Python3的視訊學習的,課後題也是跟隨每一課所附屬的題目來做的,根據自己的理解和標準答案記錄的筆記。 第二十三課與二十四課 測試題: 0.使用遞迴編寫一個十進位制轉換為二進位制的函式(要求採用“除2取餘”的方式,結果與呼叫b

opencv學習:圓檢測

檢測原理: 參考連結:https://www.cnblogs.com/ssyfj/p/9275977.html#一houghcircles方法                     

OpenCV學習筆記——簡單的單目視覺測距嘗試

    前言:         視覺測距作為機器視覺領域內基礎技術之一而受到廣泛的關注,其在機器人領域內佔有重要的地位,廣泛應用於機器視覺定位、目標跟蹤、視覺避障等。機器視覺測量主要分為:單目視覺測量、雙目視覺測量、結構光視覺測量等。結構光由於光源的限制,應用的場合比較固定;

OpenCV學習筆記——車輛識別和跟蹤

     今天在GitHub上看到一個對車輛訓練好的模型,即xml檔案,於是拿來測試了一個效果。我用這個xml檔案對視訊中的每一幀畫面進行簡單的車輛識別定位,演示程式碼如下:import cv2 import numpy as np camera = cv2.VideoCa

學習OpenCV範例十三—GMM前景檢測

前一篇部落格中有談論到混合高斯模型GMM,但是隻是在上面的一個小應用,可能沒有很徹底的分析,大部分讀者看起來有點吃力,那麼在這篇微博中就給大家分析一下GMM在前景檢測的原理以及在OpenCV中的運用,當然長篇大論的原理我還是不全部寫出來的,依舊會貼出其他高手的部落格,他們寫

Android學習路線運用Fragment構建動態UI——創建一個Fragment

動態 app idt 文檔 部分 roi 現實 調用 android學習 你能夠把fragment看成是activity的模塊化部分。它擁有自己的生命周期,接受它自己的輸入事件,你能夠在activity執行時加入或者刪除它(有點像是一個“子activity”。你

Python學習筆記

準備 for ring 價格 python use imp pri exce 使用ElementTree解析XML文件 # 導入Python中內置的處理XML文件的模塊try: import xml.etree.cElementTree as ETexcept Imp

Java學習筆記:類型轉換和instanceof關鍵字

方法 png true feed out 實例 strong 運算符 nbsp 基本數據類型轉換: 自動類型轉換:把大類型的數據賦值給大類型的變量(此時的大小指的是容量的範圍) 1 byte b = 12; //byte是一個字節 2 int i = b; //i

Python學習 —— 前端之JavaScript

開始 mode mba html 國際 sca 執行 嵌入式 規範 轉載自http://www.cnblogs.com/liwenzhou/p/8004649.html 一、JavaScript概述   1.JavaScript的歷史 1992年Nombas開發出C-mi

Linux學習總結正則三劍客之awk

awkawk 也是流式編輯器,它比sed的功能更強大 1.截取文檔中的某段 awk -F ‘:‘ ‘{print $1}‘ /etc/passwd |head -2-F 指定分割符,不指定以空格或者tab為分隔符print 為打印動作$1 為第一字段, $2 為第二字段,依次類推,$0標示整行那麽打印整個文檔

java基礎學習總結:自己寫一個java.lang.reflect.Proxy代理的實現

     動態代理裡面用到了一個類就是java.lang.reflect.Proxy,這個類是根據代理內容為傳入的介面生成代理用的。本文就自己寫一個Proxy類出來,功能和java.lang.reflect.Proxy一樣,傳入介面、代理內容,生成代理。  

初識Leetcode----學習【打家劫舍、快樂數】

①打家劫舍 你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。 給定一個代表每個房屋存放金額的非負整數陣列,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金

nginx 原始碼學習筆記—— event 模組() ——事件驅動核心ngx_process_events_and_timers

首先繼續回憶下,之前子執行緒執行操作裡面有一個未涉及的內容ngx_process_events_and_timers,今天我們就來研究下這個函式。 本篇文章來自於:http://blog.csdn.net/lengzijian/article/details/7601730 先來看一下第十九

Effective_STL 學習筆記 永遠讓比較函式對相等的值返回 false

  除非比較函式總是為相等的值返回 false,否則將會打破所有的標準關聯容器, 不管關聯容器是否允許存在副本(set、map、multiset、multimap) 對於(set、map)使用 less_equal (<=): 1   !( 10A <= 10B ) &a

機器學習筆記:TensorFlow實戰十三遷移學習

1 - 引言 越複雜的神經網路,需要的訓練集越大,ImageNet影象分類資料集有120萬標註圖片,所以才能將152層的ResNet的模型訓練到大約96.%的正確率。但是在真正的應用中,很難收集到如此多的標註資料。即使收集到也需要花費大量人力物力來標註。並且即使有了大量的資料集,要訓練一

Javaweb學習筆記————————過濾器

過濾器     過濾器概述         1.什麼是過濾器:             過濾器javaweb三大元件之一,它與Serlvet很相似,不過它過濾器是用來攔截請求的,而不是處理             請求的。             當用戶請求某個Servlet

python學習之網站的編寫HTML,CSS,JS----------事件例如點選事件等及繫結事件的幾種方式

事件:什麼叫做事件呢,就是我們在頁面中的一些滑鼠和鍵盤操作,比如onclick就是點選事件,然後我們將介紹幾種繫結事件的方式。 1.在標籤中繫結 <div onclick="fuc()"></div> <script> fun