1. 程式人生 > >OpenCV學習筆記[5]FLANN特徵匹配

OpenCV學習筆記[5]FLANN特徵匹配

OpenCV學習筆記:FLANN特徵匹配

        本次給出FLANN特徵匹配的Java實現。

[簡介]

        特徵匹配記錄下目標影象與待匹配影象的特徵點(KeyPoint),並根據特徵點集合構造特徵量(descriptor),對這個特徵量進行比較、篩選,最終得到一個匹配點的對映集合。我們也可以根據這個集合的大小來衡量兩幅圖片的匹配程度。

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

[特徵匹配]

FeatureMatching.java:

import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.features2d.DescriptorExtractor;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.FeatureDetector;
import org.opencv.highgui.Highgui;

import com.thrblock.opencv.fm.view.ConsoleView;
import com.thrblock.opencv.fm.view.MatchingView;

public class FeatureMatching {
	private Mat src;
	private MatOfKeyPoint srcKeyPoints;
	private Mat srcDes;
	
	private FeatureDetector detector;
	private DescriptorExtractor extractor;
	private DescriptorMatcher matcher;

	private MatchingView view;
	public FeatureMatching(MatchingView view) {
		this.view = view;
		srcKeyPoints = new MatOfKeyPoint();
		srcDes = new Mat();
		detector = FeatureDetector.create(FeatureDetector.SURF);
		extractor = DescriptorExtractor.create(DescriptorExtractor.SURF);
		matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
	}

	public int doMaping(String dstPath) {
		view.setDstPic(dstPath);
		// 讀入待測影象
		Mat dst = Highgui.imread(dstPath);
		System.out.println("DST W:"+dst.cols()+" H:" + dst.rows());
		// 待測影象的關鍵點
		MatOfKeyPoint dstKeyPoints = new MatOfKeyPoint();
		detector.detect(dst, dstKeyPoints);
		// 待測影象的特徵矩陣
		Mat dstDes = new Mat();
		extractor.compute(dst, dstKeyPoints, dstDes);
		// 與原圖匹配
		
		MatOfDMatch matches = new MatOfDMatch();
		matcher.match(dstDes, srcDes, matches);
		//將結果輸入到檢視 並得到“匹配度”
		return view.showView(matches, srcKeyPoints, dstKeyPoints);
	}

	public void setSource(String srcPath) {
		view.setSrcPic(srcPath);
		// 讀取影象 寫入矩陣
		src = Highgui.imread(srcPath);
		System.out.println("SRC W:"+src.cols()+" H:" + src.rows());
		// 檢測關鍵點
		detector.detect(src, srcKeyPoints);
		// 根據源影象、關鍵點產生特徵矩陣數值
		extractor.compute(src, srcKeyPoints, srcDes);
	}

	public static void main(String[] args) {
		System.loadLibrary("opencv_java249");
		FeatureMatching mather = new FeatureMatching(new ConsoleView());
		//FeatureMatching mather = new FeatureMatching(new GEivView());
		mather.setSource("./Data/Lession5/BK.jpg");
		mather.doMaping("./Data/Lession5/BK_part_rr.png");
	}
}

MatchingView.java 該介面用來計算並輸出結果

import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;


public interface MatchingView {
	public void setDstPic(String dstPath);
	public void setSrcPic(String picPath);
	public int showView(MatOfDMatch matches,MatOfKeyPoint srcKP,MatOfKeyPoint dstKP);
}
ConsoleView.java:實現了檢視介面,將結果列印在控制檯中,如果實在沒有什麼拿手的圖形環境的話就只能看文字了。
import java.util.LinkedList;
import java.util.List;

import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.features2d.DMatch;

public class ConsoleView implements MatchingView{

	@Override
	public int showView(MatOfDMatch matches,MatOfKeyPoint srcKP,MatOfKeyPoint dstKP) {
		System.out.println(matches.rows() + " Match Point(s)");

		double maxDist = Double.MIN_VALUE;
		double minDist = Double.MAX_VALUE;
		
		DMatch[] mats = matches.toArray();
		for(int i = 0;i < mats.length;i++){
			double dist = mats[i].distance;
			if (dist < minDist) {
				minDist = dist;
			}
			if (dist > maxDist) {
				maxDist = dist;
			}
		}
		System.out.println("Min Distance:" + minDist);
		System.out.println("Max Distance:" + maxDist);
		//將“好”的關鍵點記錄,即距離小於3倍最小距離,同時給定一個閾值(0.2f),這樣不至於在毫不相干的影象上分析,可依據實際情況調整
		List<DMatch> goodMatch = new LinkedList<>();
		
		for (int i = 0; i < mats.length; i++) {
			double dist = mats[i].distance;
			if(dist < 3*minDist&&dist < 0.2f){
				goodMatch.add(mats[i]);
			}
		}
		System.out.println(goodMatch.size() + " GoodMatch Found");
		int i = 0;
		for(DMatch ma:goodMatch){
			System.out.println("GoodMatch" + "["+i+"]:" + ma.queryIdx + " TO: " + ma.trainIdx);
			i++;
		}
		return i;
	}

	@Override
	public void setDstPic(String dstPath) {}

	@Override
	public void setSrcPic(String picPath) {}
}
GEivView.java:使用自己的遊戲引擎繪製圖形介面輸出結果,如果之前有部署過GEiv系統的話可以嘗試(可參照部落格內相關文章)。
import geivcore.R;
import geivcore.UESI;
import geivcore.engineSys.texturecontroller.TextureController;
import geivcore.enginedata.canonical.CANExPos;
import geivcore.enginedata.obj.Obj;

import java.awt.Color;
import java.util.LinkedList;
import java.util.List;

import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.features2d.DMatch;
import org.opencv.features2d.KeyPoint;

import com.thrblock.util.RandomSet;

public class GEivView implements MatchingView{
	UESI UES;
	Obj srcPicObj,dstPicObj;
	public GEivView(){
		UES = new R();
		
		srcPicObj = UES.creatObj(UESI.BGIndex);
		srcPicObj.addGLImage(0, 0,TextureController.SYSTEM_DEBUG_TEXTURE);
		
		dstPicObj = UES.creatObj(UESI.BGIndex);
		dstPicObj.addGLImage(0, 0,TextureController.SYSTEM_DEBUG_TEXTURE);
	}
	@Override
	public int showView(MatOfDMatch matches, MatOfKeyPoint srcKP,
			MatOfKeyPoint dstKP) {
		System.out.println(matches.rows() + " Match Point(s)");

		double maxDist = Double.MIN_VALUE;
		double minDist = Double.MAX_VALUE;
		
		DMatch[] mats = matches.toArray();
		for(int i = 0;i < mats.length;i++){
			double dist = mats[i].distance;
			if (dist < minDist) {
				minDist = dist;
			}
			if (dist > maxDist) {
				maxDist = dist;
			}
		}
		System.out.println("Min Distance:" + minDist);
		System.out.println("Max Distance:" + maxDist);
		//將“好”的關鍵點記錄,即距離小於3倍最小距離,可依據實際情況調整
		List<DMatch> goodMatch = new LinkedList<>();
		
		for (int i = 0; i < mats.length; i++) {
			double dist = mats[i].distance;
			if(dist < 3*minDist&&dist < 0.2f){
				goodMatch.add(mats[i]);
			}
		}
		System.out.println(goodMatch.size() + " GoodMatch Found");
		
		DMatch[] goodmats = goodMatch.toArray(new DMatch[]{});
		KeyPoint[] srcKPs = srcKP.toArray();//train
		KeyPoint[] dstKPs = dstKP.toArray();//query
		
		for(int i = 0;i < goodmats.length;i++){
			Point crtD = dstKPs[goodmats[i].queryIdx].pt;
			Point crtS = srcKPs[goodmats[i].trainIdx].pt;
			showMap(dstPicObj.getDx() + crtD.x,dstPicObj.getDy() + crtD.y,srcPicObj.getDx() + crtS.x,srcPicObj.getDy() + crtS.y);
			System.out.println("MAP :("+(int)crtD.x+","+(int)crtD.y+") --->("+(int)crtS.x+","+(int)crtS.y+")");
		}
		return goodmats.length;
	}
	@Override
	public void setDstPic(String dstPath) {
		dstPicObj.setPath(dstPath,true);
		dstPicObj.setPosition(CANExPos.POS_X_LEFT,50.0f);
		dstPicObj.setPosition(CANExPos.POS_Y_CENTER);
		
		dstPicObj.show();
	}
	@Override
	public void setSrcPic(String picPath) {
		srcPicObj.setPath(picPath,true);
		srcPicObj.setPosition(CANExPos.POS_X_RIGHT,50.0f);
		srcPicObj.setPosition(CANExPos.POS_Y_CENTER);

		srcPicObj.show();
	}
	private void showMap(double x,double y,double x1,double y1){
		Color dstColor = RandomSet.getRandomColor();
		
		Obj oval = UES.creatObj(UESI.UIIndex);
		oval.addGLOval("FFFFFF",0,0,5,5,12);
		oval.setColor(dstColor);
		oval.setCentralX((float)x1);
		oval.setCentralY((float)y1);
		oval.show();
		
		oval = UES.creatObj(UESI.UIIndex);
		oval.addGLOval("FFFFFF",0,0,5,5,12);
		oval.setColor(dstColor);
		oval.setCentralX((float)x);
		oval.setCentralY((float)y);
		oval.show();
		
		Obj line = UES.creatObj(UESI.UIIndex);
		line.addGLLine("FFFFFF",(float)x,(float)y,(float)x1,(float)y1);
		line.setLineWidth(2.0f);
		line.setColor(dstColor);
		line.setAlph(0.5f);
		line.show();
	}
}

[測試用例]

原圖還是我們之前用的“貝殼”


匹配圖,它來自原圖轉置後擷取的一部分:


[測試結果]

[控制檯檢視]

輸出內容:

SRC W:358 H:300
DST W:156 H:85
84 Match Point(s)
Min Distance:0.06136654317378998//所謂關鍵點的“距離”指的是兩個關鍵點間的匹配程度,越小越匹配
Max Distance:0.4693795144557953
25 GoodMatch Found//共發現25個“好”的匹配點
GoodMatch[0]:0 TO: 6//這裡的0->6意思是關鍵點集合中的對映關係,即KeyPoint[0]->KeyPoint[6]
GoodMatch[1]:1 TO: 23
GoodMatch[2]:3 TO: 30
GoodMatch[3]:4 TO: 20
GoodMatch[4]:5 TO: 23
GoodMatch[5]:6 TO: 27
GoodMatch[6]:7 TO: 19
GoodMatch[7]:9 TO: 73
GoodMatch[8]:10 TO: 65
GoodMatch[9]:12 TO: 96
GoodMatch[10]:14 TO: 38
GoodMatch[11]:15 TO: 97
GoodMatch[12]:19 TO: 162
GoodMatch[13]:20 TO: 175
GoodMatch[14]:22 TO: 164
GoodMatch[15]:29 TO: 247
GoodMatch[16]:31 TO: 283
GoodMatch[17]:33 TO: 155
GoodMatch[18]:36 TO: 261
GoodMatch[19]:39 TO: 218
GoodMatch[20]:45 TO: 717
GoodMatch[21]:48 TO: 487
GoodMatch[22]:60 TO: 150
GoodMatch[23]:68 TO: 91
GoodMatch[24]:77 TO: 1036

[GEiv檢視]


↑這樣更直觀一些,可以看出大部分對映關係是正確的。

[總結]

        對影象識別領域的一些概念進行了瞭解,包括特徵點與特徵量這樣的敘述手段,但問題還是很多,例如特徵點的計算依據(輪廓?拐點?)等,我希望在以後的學習中找到答案。

相關推薦

OpenCV學習筆記[5]FLANN特徵匹配

OpenCV學習筆記:FLANN特徵匹配         本次給出FLANN特徵匹配的Java實現。 [簡介]         特徵匹配記錄下目標影象與待匹配影象的特徵點(KeyPoint),並根據特徵點集合構造特徵量(descriptor),對這個特徵量進行比較、篩選,

opencv-學習筆記(5)形態學轉變

中心 sds TE inf 輪廓 port IE jpg import               opencv-學習筆記(4)形態學轉變 本章講了幾種形態學操作 腐蝕erode 膨脹dilate 開運算MORPH_OPEN 閉運算MORPH_CLOSE 形態學梯度MOR

Python+OpenCV學習(12)---特徵匹配

利用python學習OpenCV,個人感覺比較方便。函式的形式與C++基本相同,所以切換過來還是比較好的,對於像我這種對python不太熟練的人,使用python的整合開發環境PyCharm進行學習,可以設定斷點除錯,有助於我這類初學者理解掌握。 下面是利用python

OpenCV學習筆記——用haar特徵訓練自己的分類器(再做手勢檢測)

資料還是得看啊,又讀了經典文獻《Robust Real-Time Face Detection》,不願意讀原文的朋友可以看看http://blog.csdn.net/hqw7286/article/details/5556767,作者把文中的要點基本也都總結出來了。Ope

OpenCv學習筆記11--SURF特徵提取演算法

此opencv系列部落格只是為了記錄本人對<<opencv3計算機視覺-pyhton語言實現>>的學習筆記,所有程式碼在我的github主頁https://github.com/RenDong3/OpenCV_Notes. 歡迎star,不定時更新.

opencv學習筆記二十九:SIFT特徵點檢測與匹配

SIFT(Scale-invariant feature transform)是一種檢測區域性特徵的演算法,該演算法通過求一幅圖中的特徵點(interest points,or corner points)及其有關scale 和 orientation 的描述子得到特徵並進行

opencv學習筆記三十六:AKAZE特徵點檢測與匹配

KAZE是日語音譯過來的 , KAZE與SIFT、SURF最大的區別在於構造尺度空間,KAZE是利用非線性方式構造,得到的關鍵點也就更準確(尺度不變性 ); Hessian矩陣特徵點檢測 ,方向指定,基於一階微分影象(旋轉不變性 ) ; 描述子生成 ,歸一化處理(光照不變

OpenCV學習筆記】三十七、特徵檢測與匹配(二)——SIFT特徵匹配

特徵檢測與匹配(二)——SIFT特徵點匹配 1.SIFT特徵點提取 2.繪製特徵點 3.特徵點描述符(特徵向量)提取 4.使用暴力匹配器進行暴力匹配 5.對匹配結果進行篩選(依據DMatch結構體中的float型別變數distance進行篩選) 6.繪製匹配結果 先上ppt

OpenCV學習筆記__特徵檢測與匹配之 SURF演算法

SURF 演算法  ——“加速版的具有魯棒性的特徵”演算法 步驟: 特徵檢測 —— 特徵描述 —— 特徵匹配 實現流程: (1)特徵檢測:SurfFeatureDetector類 . detec

OpenCV學習筆記】三十、輪廓特徵屬性及應用(七)—位置關係及輪廓匹配

輪廓特徵屬性及應用(七)—位置關係及輪廓匹配 1.計算點與輪廓的距離及位置關係——pointPolygonTest() 2.矩的計算——moments() 3.形狀匹配(比較兩個形狀或輪廓間的相似度)

OpenCV學習筆記(30)KAZE 演算法原理與原始碼分析(四)KAZE特徵的效能分析與比較

      KAZE系列筆記: 1.  OpenCV學習筆記(27)KAZE 演算法原理與原始碼分析(一)非線性擴散濾波 2.  OpenCV學習筆記(28)KAZE 演算法原理與原始碼分析(二)非線性尺度空間構

Less學習筆記5匹配模式

匹配模式:類似於JS中的if語句,但不完全是,滿足一定條件後才能匹配 比如:用CSS去畫一個三角 <div class='triangle'></div> .triangle{     width: 0;    &

opencv學習筆記三十二:Haar特徵與積分影象

一、 Haar特徵定義         Haar特徵是基於“塊”的特徵,也被稱為矩形特徵。Haar特徵(模板)分為三類:邊緣特徵、線性特徵、中心特徵和對角線特徵。特徵模板內有白色和黑色兩種矩形,並定義該模板的特徵值為白色矩形畫素和減去黑色矩形畫素和。Haar特徵值反映了影象

OpenCv-C++-平面物件識別(接FLANN特徵匹配)

本文接著上篇FLANN特徵匹配,從上篇可以知道,如果特徵匹配時全部是用線進行匹配,那麼真的讓人看著很窩心。那麼,可不可以把匹配到的結果用矩形或圓表示出來呢?當然可以,這就是平面物件識別。是上一章節的更進一步。這裡主要用到兩個新的API: 1、findHomography() ------&g

OpenCv-C++-FLANN特徵匹配演算法

FLANN(快速最近鄰逼近搜尋函式庫),它是一個演算法庫,包含一系列演算法庫,OpenCv中集成了FLANN的一部分匹配演算法,主要對高維資料搜尋比較快。 相比於上一篇的暴力匹配演算法,FLANN更加精確,不會有過多的描述特徵匹配到。 匹配基本步驟: 檢測->提取->計算得到描述

OpenCV學習筆記】之影象輪廓特徵與影象的矩

轉載: https://blog.csdn.net/zhu_hongji/article/details/81699736   一、影象的輪廓(Contours of Image)        輪廓可以說是一個很好的影象目標的

OpenCV學習筆記(十七)模板匹配

我的目的就是選取影象中的指示燈,識別那種功能的指示燈亮了,進而知道那種功能打開了。 模板匹配: 模板匹配是傻瓜似的識別,從一副影象中尋找與模板影象相似部分的技術。模板匹配由matchTemplate()函式完成。 介紹兩個函式,matchTemplate()和minMaxL

OpenCV學習筆記(三十三)——用haar特徵訓練自己的分類器(再做手勢檢測)

資料還是得看啊,又讀了經典文獻《Robust Real-Time Face Detection》,不願意讀原文的朋友可以看看http://blog.csdn.net/hqw7286/article/details/5556767,作者把文中的要點基本也都總結出來了。Ope

OpenCV學習筆記[4]模板匹配

OpenCV學習筆記:模板匹配 Java version         首先我要糾正一個錯誤的學習習慣,像OpenCV這樣的大型庫,按照官方教程一步一步除錯的學習效率太低了,OpenCV就像字典一樣,當我們需要計算機進行某些視覺特性模擬時,針對具體問題去檢索庫中對應的A

OpenCv學習筆記10--尺度不變特徵(SIFT)

此opencv系列部落格只是為了記錄本人對<<opencv3計算機視覺-pyhton語言實現>>的學習筆記,所有程式碼在我的github主頁https://github.com/RenDong3/OpenCV_Notes. 歡迎star,不定時更新.