• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • open cvFLANN特征檢測

    標簽: 溫職 THINK TWICE party  計算機視覺

    什么是FLANN特征匹配

    1.FLANN是快速最近鄰搜索包(Fast_Library_for_Approximate_Nearest_Neighbors)的簡稱 即實現快速高效匹配
    FlannBasedMatcher中FLANN的含義是Fast Library forApproximate Nearest Neighbors,從字面意思可知它是一種近似法
    算法更快但是找到的是最近鄰近似匹配,所以當我們需要找到一個相對好的匹配但是不需要最佳匹配的時候往往使用FlannBasedMatcher
    當然也可以通過調整FlannBasedMatcher的參數來提高匹配的精度或者提高算法速度,但是相應地算法速度或者算法精度會受到影響

    2.FLANN特征匹配利用近似k近鄰算法去尋找一致性,FLANN方法比BF(Brute-Force)方法快的多


    FLANN特征匹配的步驟
    1.特征匹配記錄下目標圖像與待匹配圖像的特征點(KeyPoint),
    2.并根據特征點集合構造特征量(descriptor),對這個特征量進行比較篩選
    3.最終得到一個匹配點的映射集合
    4.我們也可以根據這個集合的大小來衡量兩幅圖片的匹配程度


    注 :關于目前了解到的圖像的各種特征點的關系
    corners:包含大量本地信息的像素塊,并能夠在另一張圖中被快速識別
    keypoints:作為 corners 的擴展,它將像素塊的信息進行編碼從而使得更易辨識,至少在原則上唯一
    descriptors:它是對 keypoints 進一步處理的結果。通常它具有更低的維度,從而使得圖像塊能夠在另一幅不同的圖像中被更快地識別

    特征匹配與模板匹配不同,由于是計算特征點集合的相關度,轉置操作對匹配影響不大 但它容易受到失真、縮放的影響


    FlannBasedMatcher的構造函數如下:

    class FlannBasedMatcher : public DescriptorMatcher
    {	public:
    	FlannBasedMatcher(
    	const Ptr<flann::IndexParams>& indexParams=new flann::KDTreeIndexParams(),
    	const Ptr<flann::SearchParams>& searchParams=new flann::SearchParams() );
    
    	virtual void add( const vector<Mat>& descriptors );
    	virtual void clear();
    
    	virtual void train();
    	virtual bool isMaskSupported() const;
    
    	virtual Ptr<DescriptorMatcher> clone( bool emptyTrainData=false ) const;
    	protected:
    };
    

    FLANN特征匹配的原理

    1.特征匹配的結果會得到兩個特征集合的對應關系列表 第一組特征集被稱為訓練集(train),第二組被稱為查詢集(query)
    2.Flann 在調用匹配函數之前,為了提高匹配速度,訓練一個匹配器。訓練階段是為了優化cv::FlannBasedMatcher的性能
    3.train類將會建立特征集的索引樹。將 query image 的每一個特征點和 train 匹配器進行匹配,找出最佳匹配
    也就是從query image 的特征中逐個去和訓練器做匹配,也就是說每一個query image 特征點都會有一個最佳匹配
    后期還需要驗證這匹配的正確性,可以通過設置截斷值來去除誤差大的匹配


    關于歐式距離和漢明距離
    根據描述子的不同,可以選擇不同的距離度量。如果是浮點類型的描述子,可以使用其歐式距離;對于二進制的描述子(BRIEF)可以使用其漢明距離(兩個不同二進制之間的漢明距離指的是兩個二進制串不同位的個數)

    關于匹配方法
    交叉匹配
    針對暴力匹配,可以使用交叉匹配的方法來過濾錯誤的匹配 交叉過濾的思想很簡單,再進行一次匹配,反過來使用被匹配到的點進行匹配,
    如果匹配到的仍然是第一次匹配的點的話,就認為這是一個正確的匹配。舉例來說就是,假如第一次特征點A使用暴力匹配的方法,
    匹配到的特征點是特征點B;反過來,使用特征點B進行匹配,如果匹配到的仍然是特征點A,則就認為這是一個正確的匹配,
    否則就是一個錯誤的匹配。OpenCV中BFMatcher已經封裝了該方法,創建BFMatcher的實例時,第二個參數傳入true即可,
    BFMatcher bfMatcher(NORM_HAMMING,true)

    KNN匹配
    K近鄰匹配,在匹配的時候選擇K個和特征點最相似的點,如果這K個點之間的區別足夠大,則選擇最相似的那個點作為匹配點
    通常選擇K = 2,也就是最近鄰匹配。對每個匹配返回兩個最近鄰的匹配,如果第一匹配和第二匹配距離比率足夠大(向量距離足夠遠),
    則認為這是一個正確的匹配,比率的閾值通常在2左右

    #include <opencv2/opencv.hpp>
    #include <opencv2/highgui/highgui_c.h>
    #include <opencv2/xfeatures2d.hpp>
    #include <iostream>
    #include <math.h>
    
    using namespace cv;
    using namespace std;
    using namespace cv::xfeatures2d;
    
    Mat src, src2,dst;
    void FLANNdetector(int, void*);
    int main()
    {
    
    	src = imread("D:/實驗臺/機器視覺/測試圖片/暴力匹配.jpg",IMREAD_GRAYSCALE);
    	src2 = imread("D:/實驗臺/機器視覺/測試圖片/暴力匹配2.jpg",IMREAD_GRAYSCALE);
    	if (src.empty()|| src2.empty())//如果src這個數據庫屬性為空
    	{
    		cout << "無法打開" << endl;
    		return -1;
    	}
    	imshow("原圖", src);
    	imshow("原圖2", src2);
    	FLANNdetector(0, 0);
    	waitKey(0);
    	return 0;
    }
    
    
    void FLANNdetector(int, void*)
    {
     	
    	//todoSURF特征檢測配合FlannBasedMatcher快速最近鄰搜索匹配算法 進行特征點匹配
    	int minHessian = 400;//特征檢測器閾值
    	Ptr<SURF> detector = SURF::create(minHessian);//構建SURF類的特征點檢測器
    	//聲明類型SURF類的 指針類名對象detector 為 SURF類中的 成員函數create構建的特征檢測器
    	//.xfeatures庫中 類SURF 中 成員函數create() 參數說明:
    	//static Ptr<SURF> create
    	//1.double hessianThreshold -- hessian關鍵點檢測器的閾值,默認在300-500之間(此值越大 特征點數量越小)
    	//2.int nOctaves (默認為4)--   表示在4個尺度空間(用高斯尺度變化來模擬SIFR金字塔縮放,這里表示縮放級數,一般四級足夠用了 越小特征點越少)
    	//3.int nOctaveLayers(默認為3)  -- 表示每個尺度空間的層數  每個尺度空間的層數最少三層  (層數越小特征點越少)
    	//4.bool extended(默認為false) -- 擴展描述符標志(true使用擴展的128個元素的描述符,false使用64個元素的描述符)
    	//5.bool upright (默認為false)-- 計算旋轉不變性的標志(true(1)不計算方向,false(0)計算方向  不計算速度更快)
    
    	vector<KeyPoint>keypointOBJ; // 設置用于存放第一張圖特征點信息的 KeyPoint類的集合向量 keypointOBJ
    	vector<KeyPoint>keypointSCENE;//同上 待匹配的第二張圖的特征點信息keypointSCENE
    
    	Mat descriptorOBJ,descriptorSCENE; //建立兩個描述符集矩陣分別存放圖一的查詢描述符集和圖二的訓練描述符集合
    
    	//進行對兩張需要進行匹配的圖像的特征提取和描述符信息的計算
    	detector->detectAndCompute(src, Mat(), keypointOBJ, descriptorOBJ);
    	detector->detectAndCompute(src2, Mat(), keypointSCENE, descriptorSCENE);
    	//todocv :: Feature2D :: detectAndCompute檢測特征點并計算其描述子的函數(使用detector這個SURF特征檢測器)
    	//InputArray 	image  -- 輸入的需特征檢測的圖像
    	//InputArray 	mask  -- ROI區域掩膜操作(默認為Mat() 不進行掩膜)
    	//std::vector< KeyPoint > & keypoints  -- 輸出檢測出的特征點的Keypoint類信息向量數組到對應的集合向量數組中
    	//OutputArray 	descriptors  -- 輸出計算出的描述符信息(矩陣)到對應的描述符數組中
    	//bool seProvidedKeypoints = false -- 是否運行特征點的檢測,該值為true表示不進行特征點的檢測,而只是使用提供的特征點進行特征點描述符的運算
    
    	//基于Flann類型的描述符匹配器  
    	FlannBasedMatcher matcher;
    	//第一個參數是IndexParams,對于SIFT和SURF,可以傳入參數index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
    	//對于ORB,可以傳入參數index_params = dict(algorithm = FLANN_INDEX_LSH, table_number = 6, key_size = 12, multi_probe_level = 1
    	//第二個參數是SearchParams,可以傳入參數search_params = dict(checks = 100),它來指定遞歸遍歷的次數,值越高結果越準確,但是消耗的時間也越多
    
    
    	//todo建立未過濾前的 匹配項描述符集 所需要的動態向量DMatch類數組 matches
    	vector<DMatch>matches;//未進行篩選前的匹配描述符
    	/*此構造類有四個元素
    	CV_PROP_RW int -    queryIdx -- 對應的查詢圖像的特征描述子索引
    	CV_PROP_RW int-     trainIdx-- 對應的訓練(模板)圖像的特征描述子索引
    	CV_PROP_RW int-	    imgIdx-- 訓練圖像的索引(若有多個訓練圖像 指定要匹配哪一個訓練圖像)
    	CV_PROP_RW float -distance -- 兩個匹配的特征向量之間的歐氏距離,越小表明匹配度越高	*/
    
    	matcher.match(descriptorOBJ,descriptorSCENE,matches);
    	//todovoid cv::DescriptorMatcher::match使用建立好的FLANN匹配器FlannBasedMatcher 進行最優匹配查找
    	//InputArray 	queryDescriptors  -- 查詢描述符集 一般為cv::Mat類型 集內為某個特征提取的描述符
    	//InputArray 	trainDescriptors  -- 訓練描述集 一般為cv::Mat類型 集內為某個特征提取的描述符(此處輸入的應該時未進入類對象集合內 自己訓練的集合)
    	//std::vector< DMatch > & matches  -- 對匹配項進行匹配 輸出的匹配項描述符(匹配項的大小可能小于描述符的計數)
    	//InputArray 	mask = noArray() -- ROI區域選取 指定輸入指定區域的查詢和訓練矩陣之間允許的匹配的掩碼描述符 (默認不進行mask)
    	
    	//注:一個匹配器嘗試在一副或一組圖中匹配一幅圖中的關鍵點,如果匹配成功,將返回 cv::DMatch 的列表
    	//使用cv::FlannBasedMatcher來優化,為descriptor建立索引樹,這種操作將在匹配大量數據時發揮巨大作用
    	//而Brute-force matcher在這個過程并不進行操作,它只是將train descriptors保存在內存中
    
    	//匹配相關度最高的特征點過濾不相干的特征點
    	double minDist = 1000;//由于要篩選最小值 所以提取定義的最小值要盡可能大于被篩選的值
    	double maxDist = 0;//由于要篩選最大值 所以提取定義的最大值要盡可能小于被篩選的值
    
    	//todo通過遍歷兩個匹配的特征向量之間的歐氏距離(距離越近匹配度越高) 得出過濾算子時需要的閾值(歐式距離最小值)
    	for (int i = 0; i < descriptorOBJ.rows; i++)
    	{
    		double dist = matches[i].distance;//遍歷讀取原匹配項描述符集matches中的每對匹配項描述符的歐式距離信息
    		if (dist > maxDist)
    		{
    			maxDist = dist;//尋找原匹配項描述符集matches中的歐式距離最大的匹配項描述符
    		}
    		if (dist < minDist)
    		{
    			minDist = dist;//尋找距離最小的匹配項描述符
    		}
    	}
    	printf("最大歐氏距離為 %f\n",maxDist);//得出最大歐氏距離為 0.557437
    	printf("最小歐氏距離為 %f\n", minDist);//得出最小歐氏距離為 0.118169
    	
    	//todo建立存儲過濾歐氏距離時大于閾值的匹配描述符的DMatch類向量矩陣goodMatches 
    	vector<DMatch>goodMatches; //被壓入此矩陣內的匹配描述符信息將被認為是準確的匹配描述符進行繪制
    	/*此構造類有四個元素
    	CV_PROP_RW int -    queryIdx -- 對應的查詢圖像的特征描述子的索引
    	CV_PROP_RW int-     trainIdx-- 對應的訓練(模板)圖像的特征描述子的索引
    	CV_PROP_RW int-	    imgIdx-- 訓練圖像的索引(若有多個訓練圖像)
    	CV_PROP_RW float -distance -- 兩個特征向量之間的歐氏距離,越小表明匹配度越高*/
    
    	int Mnum = 1;
    	//todo尋找歐式距離(匹配度)符合閾值標準的匹配項描述符 放入goodMatches列表
    	for (int i = 0; i < descriptorOBJ.rows; i++)// descriptorOBJ.rows等值于matches.size()
    	{
    		double dist = matches[i].distance;//遍歷讀取原匹配項描述符集中的第i對匹配項描述符的歐式距離信息
    		if(dist<max(2*minDist,0.02))//將小于2*最小歐式距離的匹配項描述符壓入最終確認的匹配項描述符集中  最小歐式距離為0.118169 
    		{
    			cout << endl;
    			cout << "第" << Mnum << "對篩選出的查詢描述符在原描述符集中的對數序號為"<<i+1<<"索引為" << matches[i].queryIdx << endl;
    			cout << "第" << Mnum << "對篩選出的訓練描述符在原描述符集中的對數序號為"<<i+1<< "索引為" << matches[i].trainIdx << endl;
    			cout << "第" << Mnum << "對篩選出的匹配項描述符在原描述符集中的對數序號為" << i + 1 << "描述符之間最短歐式距離為" << matches[i].distance << endl;
    			goodMatches.push_back(matches[i]);//將符合篩選標注的匹配項描述符壓入 將被繪制的匹配項描述符集goodMatches中
    			Mnum++;
    		}
    	}
    	//注: 關于std::max函數 此函數有兩個參數 max(a,b) 作用是比較這兩個參數的大小并輸出大的參數(兩個參數的類型一定要一致)
    	//        有時描述子之間的最小距離非常小,可以利用max函數設置一個經驗值作為下限
    	printf("篩選出的符合標準的匹配項描述符的總數為 :%d\n",goodMatches.size());
    	
    	//打印篩選出的符合標準的匹配項描述符信息
    	for (int i = 0; i < goodMatches.size(); i++)
    	{
    		cout << endl;
    		cout << "第" << i+1 << "對篩選出的查詢描述符索引為" << goodMatches[i].queryIdx << endl;
    		cout << "第" << i+1 << "對篩選出的訓練描述符索引為" << goodMatches[i].trainIdx << endl;
    		cout << "第" << i +1<< "對篩選出的匹配項描述符之間最短歐式距離為" << goodMatches[i].distance << endl;
    	}
    	//打印此案例需要顯示的描述符信息
    	printf("未篩選前 匹配項描述符合集 matches 大小為 %d \n", matches.size());
    	printf("篩選后 匹配項描述符合集 goodMatches 大小為 %d \n", goodMatches.size());
    	printf("原圖一 row=%d,col=%d  原圖二 row=%d,col=%d \n", src.rows, src.cols, src2.rows, src2.cols);
    	printf("descriptorOBJ查詢描述符集的row=%d, col=%d  descriptorSCEN訓練描述符集的row = % d, col = % d\n", descriptorOBJ.rows, descriptorOBJ.cols, descriptorSCENE.rows, descriptorSCENE.cols);
    	printf("descriptorOBJ查詢描述符集 大小為%d   descriptorSCEN查詢描述符集大小為%d\n", descriptorOBJ.size(), descriptorSCENE.size());
    	printf("descriptorOBJ查詢描述符集 描述符元素的類型為%d   descriptorSCEN查詢描述符集 描述符元素的類型為% d\n", descriptorOBJ.type(), descriptorSCENE.type());
    
    	//顯示查詢描述符集和訓練描述符集矩陣
    	imshow("descriptorOBJ-查詢描述符集", descriptorOBJ);
    	imshow("descriptorSCENE-訓練描述符集", descriptorSCENE);
    
    	Mat MatchesIMG;
    	drawMatches(src, keypointOBJ, src2, keypointSCENE, goodMatches, MatchesIMG, Scalar::all(-1),Scalar::all(-1),vector<char>(),DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
    	//todovoid cv::drawMatches 對進行匹配的兩張圖片水平排列,然后在最佳匹配的點之間繪制直線
    	//InputArray 	img1  -- 源圖像一
    	//const std::vector< KeyPoint > & keypoints1  -- 源圖像一的關鍵點
    	//InputArray 	img2  --源圖像二
    	//const std::vector< KeyPoint > & keypoints2  -- 源圖像二的關鍵點
    	//const std::vector< DMatch > & matches1to2  -- 匹配完畢的一二兩張圖像的匹配項描述符集合(從第一個圖像匹配到第二個圖像)
    	//InputOutputArray 	outImg  -- 輸出匹配結果的圖像 (為空時 默認并排繪制輸出圖像以及連接關鍵點 若不為空 在圖像上繪制關系點)
    	//const Scalar & matchColor = Scalar::all(-1)  -- 輸出的匹配圖像的線和連接點的顏色 默認為Scalar::all(-1)隨機顏色
    	//const Scalar & singlePointColor = Scalar::all(-1)  -- 沒有匹配的關鍵點將使用此顏色 默認為Scalar::all(-1)隨機顏色
    	//const std::vector< char > & matchesMask = std::vector< char >()  --匹配上的關鍵點位置將被置一 決定哪些匹配對被畫出,如果掩膜為空,則所有匹配對都被畫出(默認為空)
    
    	//int 	flags = DrawMatchesFlags::DEFAULT -- 繪制特征點和連接線的類型 有以下四種
    	//DEFAULT:默認的選項 繪制全部匹配項 輸出結果在 outImg 中,同時用小圓圈標記
    	//DRAW_OVER_OUTIMG:并不重新分配 outImg 的空間,這樣可以多次調用 cv::drawMatches(),將結果繪制在一張圖上
    	//NOT_DRAW_SINGLE_POINTS:單點(未成功匹配)的特征點不被繪制
    	//DRAW_RICH_KEYPOINTS:繪制特征點的時候繪制的是一個個帶有方向的圓,這種方法同時顯示圖像的坐標,size,和方向,是最能顯示特征信息的一種繪制方式
    
    	imshow("FLANN特征并進行了匹配度篩選后的結果",MatchesIMG);
    }
    

    SURF特征檢測器+FLANN特征匹配+匹配度(歐氏距離)篩選的效果
    在這里插入圖片描述
    SURF特征檢測器+暴力匹配的結果
    在這里插入圖片描述

    版權聲明:本文為Chroniccandy原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/Chroniccandy/article/details/107621359

    智能推薦

    目標檢測:使用Open_cv在圖像上批量畫bounding box

    本次使用的庫具體版本為: Python 3.6.3 Open_cv 3.3.0 Numpy 1.14.2 Win10 1709教育版 1.問題提出     在之前使用Mask_RCNN檢測之后,把檢測到的坐標保存到了一個csv中,最近想看一下生成的bounding box準確與否,于是今天說一下如何使用Open_cv在圖像上批量畫bounding box! ...

    3D游戲編程與設計——游戲對象與圖形基礎章節作業與練習

    3D游戲編程與設計——游戲對象與圖形基礎章節作業與練習 3D游戲編程與設計——游戲對象與圖形基礎章節作業與練習 自學資源 作業內容 1、基本操作演練【建議做】 天空盒的制作: 地圖的制作: 整體效果: 2、編程實踐 項目要求: 項目結構: 代碼詳解: Actions: ISSActionCallback.cs SSAction.cs SSAction...

    FlycoTabLayout 的使用

    FlycoTabLayout 一個Android TabLayout庫,目前有3個TabLayout SlidingTabLayout:參照PagerSlidingTabStrip進行大量修改. 新增部分屬性 新增支持多種Indicator顯示器 新增支持未讀消息顯示 新增方法for懶癌患者 CommonTabLayout:不同于SlidingTabLayout對ViewPager依賴,它是一個不...

    爬蟲項目實戰八:爬取天氣情況

    爬取天氣情況 目標 項目準備 接口分析 代碼實現 效果顯示 寫入本地 目標 根據天氣接口,爬取接下來一周的天氣情況。 項目準備 軟件:Pycharm 第三方庫:requests,BeautifulSoup,csv 接口地址:http://api.k780.com:88/?app=weather.future&weaid=城市名&appkey=10003&sign=b59bc...

    關于web項目的目錄問題

    先給段代碼: 上面這個代碼一直出錯,我不知道原因,后面不停的查找資料發現了問題:我的web項目輸出目錄有問題,因為我也是第一次用idea寫web項目,發現很多bug 其實都沒有太大問題,我們需要注意的是你必須在out這個輸出文件夾中擁有這個文件,out輸出文件夾會默認過濾這些文件...

    猜你喜歡

    二叉搜索樹轉化為雙向鏈表

    題目描述: 輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的循環雙向鏈表。要求不能創建任何新的節點,只能調整樹中節點指針的指向。 為了讓您更好地理解問題,以下面的二叉搜索樹為例: 我們希望將這個二叉搜索樹轉化為雙向循環鏈表。鏈表中的每個節點都有一個前驅和后繼指針。對于雙向循環鏈表,第一個節點的前驅是最后一個節點,最后一個節點的后繼是第一個節點。 下圖展示了上面的二叉搜索樹轉化成的鏈表。&ldqu...

    Cocos2d-x 2.0 網格動畫深入分析

    [Cocos2d-x相關教程來源于紅孩兒的游戲編程之路CSDN博客地址:http://blog.csdn.net/honghaier] 紅孩兒Cocos2d-X學習園地QQ2群:44208467加群寫:Cocos2d-x 紅孩兒Cocos2d-X學習園地QQ群:249941957[暫滿]加群寫:Cocos2d-x 本章為我的Cocos2d-x教程一書初稿。望各位看官多提建議! Cocos2d-x ...

    vue 子組件傳值父組件 emit

    vue 子組件傳值父組件  emit    ...

    解決Python數據可視化中文部分顯示方塊問題

    一、問題 代碼如下,發現標題的中文顯示的是方塊 如下圖 二、解決方法 一般數據可視化使用matplotlib庫,設置中文字體可以在導入之后添加兩句話(這里的SimHei指的是黑體,KaiTi指的是楷體) 三、效果 1.黑體: 2.楷體: 具體的其他字體可以在matplotlib\mpl-data\fonts\ttf找到~ 四、Windows的常用字體 黑體、楷體、仿宋是可以用的,其他的字體可能需要...

    Linux的LVM掛載(Centos)

    LVM掛載 1、虛擬機添加新增磁盤(如已添加可略過) 2、查看是否有新的硬盤 3、對磁盤分區 4、LVM磁盤創建 參考地址: https://blog.51cto.com/11555417/2158443 1、虛擬機添加新增磁盤(如已添加可略過) 1.點擊虛擬機,選擇硬盤,點擊添加,選擇SCSI硬盤,添加硬盤(如下圖所示)。 2、查看是否有新的硬盤 可以看到 /dev/sdb 是我們新建的磁盤5G...

    精品国产乱码久久久久久蜜桃不卡