SIFT特征提取與檢索



一、SIFT特征簡介:
1.1算法簡介: 
        尺度不變特征轉換即SIFT (Scale-invariant feature transform)是一種計算機視覺的算法。它用來偵測與描述影像中的局部性特征,它在空間尺度中尋找極值點,並提取出其位置、尺度、旋轉不變量。
        局部影像特征的描述與偵測可以幫助辨識物體,SIFT特征是基於物體上的一些局部外觀的興趣點而與影像的大小和旋轉無關。對於光線、噪聲、些微視角改變的容忍度也相當高。基於這些特性,它們是高度顯著而且相對容易擷取,在母數龐大的特征數據庫中,很容易辨識物體而且鮮有誤認。使用 SIFT特征描述對於部分物體遮蔽的偵測率也相當高,甚至只需要3個以上的SIFT物體特征就足以計算出位置與方位。在現今的電腦硬件速度下和小型的特征數據庫條件下,辨識速度可接近即時運算。SIFT特征的信息量大,適合在海量數據庫中快速准確匹配。
      實質:歸為不同尺度空間上查找特征點(關鍵點)的問題
1.2 算法特性:

1.3  SIFT算法特點:

●SIFT特征是圖像的局部特征,其對旋轉、尺度縮放、亮度變化保持不變性,對視角變化、仿射變換、噪聲也保持一定程度的穩定性。
●獨特性(Distinctiveness)好,信息量豐富,適用於在海量特征數據庫中進行快速、准確的匹配。
●多量性,即使少數的幾個物體也可以產生大量SIFT特征向量。
●經過優化的SIFT算法可滿足- -定的速度需求。
●可擴展性,可以很方便的與其他形式的特征向量進行聯合。

二、 SIFT算法實現特征匹配的主要三個步驟:

  1. 構建DOG尺度空間
  2. 關鍵點搜索和定位
  3. 方向賦值和特征描述的生成

1.1構建DOG尺度空間

模擬圖像數據的多尺度特征,大尺度輪廓特征,小尺度細節特征。通過構建高斯金字塔(每一層用不同的參數σ做高斯模糊(加權)),保證圖像在任何尺度都能有對應的特征點,即保證尺度不變性。

(1)圖像尺度空間:在一定的范圍內,無論物體是大還是小,人眼都可以分辨出來,然而計算機要有相同的能力卻很難,所以要讓機器能夠對物體在不同尺度下有一個統一的認知,就需要考慮圖像在不同的尺度下都存在的特點。尺度空間的獲取通常使用高斯模糊來實現,不同σ的高斯函數決定了對圖像的平滑程度,越大的σ值對應的圖像越模糊。

 

(2)多分辨率金字塔:圖像金字塔是同一圖像在不同的分辨率下得到的一組結果,一個傳統的金字塔中,每一層的圖像是其上一層圖像長、高的各一半。

多分辨率的圖像金字塔雖然生成簡單,但其本質是降采樣,圖像的局部特征則難以保持,也就是無法保持特征的尺度不變性。

通過圖像的模糊程度來模擬人在距離物體由遠到近時物體在視網膜上成像過程,距離物體越近其尺寸越大圖像也越模糊,這就是高斯尺度空間。通過結合高斯尺度空間和多分辨率金字塔可以檢測出在不同的尺度下都存在的特征點。

2.關鍵點搜索和定位

(1)關鍵點搜索

為了尋找尺度空間的極值點,每個像素點要和其圖像域(同一尺度空間)和尺度域(相鄰的尺度空間)的所有相鄰點進行比較,當其大於(或者小於)所有相鄰點時,該點就是極值點。如下圖所示,中間的檢測點要和其所在圖像的3×3鄰域8個像素點,以及其相鄰的上下兩層的3×3領域18個像素點,共26個像素點進行比較。

 
 

(2) 刪除不好的極值點(特征點)

通過比較檢測得到的DoG的局部極值點實在離散的空間搜索得到的,由於離散空間是對連續空間采樣得到的結果,因此在離散空間找到的極值點不一定是真正意義上的極值點,因此要設法將不滿足條件的點剔除掉。可以通過尺度空間DoG函數進行曲線擬合尋找極值點,這一步的本質是去掉DoG局部曲率非常不對稱的點
要剔除掉的不符合要求的點主要有兩種:

  1. 低對比度的特征點
  2. 不穩定的邊緣響應點

    剔除低對比度的特征點

3. 生成特征描述

通過以上的步驟已經找到了SIFT特征點位置、尺度和方向信息,下面就需要使用一組向量來描述關鍵點也就是生成特征點描述子,這個描述符不只包含特征點,也含有特征點周圍對其有貢獻的像素點。描述子應具有較高的獨立性,以保證匹配率
特征描述符的生成大致有三個步驟:

  1. 校正旋轉主方向,確保旋轉不變性。
  2. 生成描述子,最終形成一個128維的特征向量
  3. 歸一化處理,將特征向量長度進行歸一化處理,進一步去除光照的影響。

為了保證特征矢量的旋轉不變性,要以特征點為中心,在附近鄰域內將坐標軸旋轉θθ (特征點的主方向)角度,即將坐標軸旋轉為特征點的主方向。旋轉后鄰域內像素的新坐標為:

 

旋轉后以主方向為中心取 8×88×8 的窗口。下圖所示,左圖的中央為當前關鍵點的位置,每個小格代表為關鍵點鄰域所在尺度空間的一個像素,求取每個像素的梯度幅值與梯度方向,箭頭方向代表該像素的梯度方向,長度代表梯度幅值,然后利用高斯窗口對其進行加權運算。最后在每個4×44×4 的小塊上繪制8個方向的梯度直方圖,計算每個梯度方向的累加值,即可形成一個種子點,如右圖所示。每個特征點由4個種子點組成,每個種子點有8個方向的向量信息。這種鄰域方向性信息聯合增強了算法的抗噪聲能力,同時對於含有定位誤差的特征匹配也提供了比較理性的容錯性。
描述子生成
與求主方向不同,此時每個種子區域的梯度直方圖在0-360之間划分為8個方向區間,每個區間為45度,即每個種子點有8個方向的梯度強度信息。
在實際的計算過程中,為了增強匹配的穩健性,Lowe建議

對每個關鍵點使用4×44×4 共16個種子點來描述,這樣一個關鍵點就可以產生128維的SIFT特征向量。
這里寫圖片描述

通過對特征點周圍的像素進行分塊,計算塊內梯度直方圖,生成具有獨特性的向量,這個向量是該區域圖像信息的一種抽象,具有唯一性。

代碼實現:
import cv2 
import numpy as np

img = cv2.imread('test_1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 得到特征點
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray, None)

img = cv2.drawKeypoints(gray, kp, img)

cv2.imshow('drawKeypoints', img)
cv2.waitKey(0)
cv2.destroyAllWindows() 
 
 
小結:

SIFT特征以其對旋轉、尺度縮放、亮度等保持不變性,是一種非常穩定的局部特征,在圖像處理和計算機視覺領域有着很重要的作用,其本身也是非常復雜的,下面對其計算過程做一個粗略總結。

  1. DoG尺度空間的極值檢測。 首先是構造DoG尺度空間,在SIFT中使用不同參數的高斯模糊來表示不同的尺度空間。而構造尺度空間是為了檢測在不同尺度下都存在的特征點,特征點的檢測比較常用的方法是Δ2G(高斯拉普拉斯LoG),但是LoG的運算量是比較大的,Marr和Hidreth曾指出,可以使用DoG(差分高斯)來近似計算LoG,所以在DoG的尺度空間下檢測極值點。

  2. 刪除不穩定的極值點。主要刪除兩類:低對比度的極值點以及不穩定的邊緣響應點。

  3. 確定特征點的主方向。以特征點的為中心、以3×1.5σ為半徑的領域內計算各個像素點的梯度的幅角和幅值,然后使用直方圖對梯度的幅角進行統計。直方圖的橫軸是梯度的方向,縱軸為梯度方向對應梯度幅值的累加值,直方圖中最高峰所對應的方向即為特征點的方向。

  4. 生成特征點的描述子。 首先將坐標軸旋轉為特征點的方向,以特征點為中心的16×16的窗口的像素的梯度幅值和方向,將窗口內的像素分成16塊,每塊是其像素內8個方向的直方圖統計,共可形成128維的特征向量。

 

三、SIFT特征匹配:

現在,使用SIFT功能進行特征匹配。為此,用兩張同一場景不同角度的圖像,它們是從不同位置拍攝的。

如下為所用的兩張圖像:

SIFT代碼

import cv2
import numpy as np
img = cv2.imread('C:/Users/w/PycharmProjects/untitled2/11.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray, None)  # 找到關鍵點
img = cv2.drawKeypoints(gray, kp, img)  # 繪制關鍵點
cv2.imshow('sp', img)
cv2.waitKey(0)
SIFT Code

Harris代碼:

import cv2
import numpy as np
filename = 'C:/Users/w/PycharmProjects/sift/picture/11.jpg'
img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
#圖像轉換為float32
dst = cv2.cornerHarris(gray,2,3,0.04)
#result is dilated for marking the corners, not important
dst = cv2.dilate(dst,None)#圖像膨脹
# Threshold for an optimal value, it may vary depending on the image.
#print(dst)
#img[dst>0.00000001*dst.max()]=[0,0,255] #可以試試這個參數,角點被標記的多余了一些
img[dst>0.01*dst.max()]=[0,0,255]#角點位置用紅色標記
#這里的打分值以大於0.01×dst中最大值為邊界
cv2.imshow('dst',img)
if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()
Harris Code

 結果顯示:


小結:

      SIFT特征檢測:SIFT從理論上說是一種相似不變量,即對圖像尺度變化和旋轉是不變量。然而,由於構造SIFT特征時,在很多細節上進行了特殊處理,使得SIFT對圖像的復雜變形和光照變化具有了較強的適應性,同時運算速度比較快,定位精度比較高。如:
  (1)在多尺度空間采用DOG算子檢測關鍵點,相比傳統的基於LOG算子的檢測方法,運算速度大大加快;
  (2)關鍵點的精確定位不僅提高了精度,而且大大提高了關鍵點的穩定性;
  (3)在構造描述子時,以子區域的統計特性,而不是以單個像素作為研究對象,提高了對圖像局部變形的適應能力;

  Harris特征檢測:在計算機視覺中,通常需要找出兩幀圖像的匹配點,如果能找到兩幅圖像如何相關,就能提取出兩幅圖像的信息。我們說的特征的最大特點就是它具有唯一可識別這一特點,圖像特征的類型通常指邊界、角點(興趣點)、斑點(興趣區域)。角點就是圖像的一個局部特征,應用廣泛。harris角點檢測是一種直接基於灰度圖像的角點提取算法,穩定性高,尤其對L型角點檢測精度高,但由於采用了高斯濾波,運算速度相對較慢,角點信息有丟失和位置偏移的現象,而且角點提取有聚簇現象。

本次實驗圖片集:

四、SIFT圖片檢索匹配:

代碼實現:

from PIL import Image
from pylab import *
from PCV.localdescriptors import sift
import matplotlib.pyplot as plt


im1f = 'C:/Users/lenovo/PycharmProjects/untitled/圖片集/16.jpg'
im1 = array(Image.open(im1f))
sift.process_image(im1f, 'out_sift_1.txt')
l1, d1 = sift.read_features_from_file('out_sift_1.txt')

arr=[]
arrHash = {}
for i in range(1,15):

    im2f = (r'C:/Users/lenovo/PycharmProjects/untitled/圖片集'+str(i)+'.jpg')
    im2 = array(Image.open(im2f))
    sift.process_image(im2f, 'out_sift_2.txt')
    l2, d2 = sift.read_features_from_file('out_sift_2.txt')
    matches = sift.match_twosided(d1, d2)
    length=len(matches.nonzero()[0])
    length=int(length)
    arr.append(length)
    arrHash[length]=im2f

arr.sort()
arr=arr[::-1]
arr=arr[:3]
i=0
plt.figure(figsize=(5,12))
for item in arr:
    if(arrHash.get(item)!=None):
        img=arrHash.get(item)
        im1 = array(Image.open(img))
        ax=plt.subplot(511 + i)
        ax.set_title('{} matches'.format(item))
        plt.axis('off')
        imshow(im1)
        i = i + 1

plt.show()

2.實驗結果

檢索的圖片

運行結果

小結:由實驗結果可以看出,sift算法不受圖片角度,大小的影響,仍有較高的檢測效率,具有非常強的穩健性。

 

 實驗四、匹配地理標記圖像

1.圖像分類
       圖片地理標記即圖像分類,可以實現多張圖片進行特定的標志性物體分類。主要步驟需要對圖像提取局部描述算子,一般情況都是使用SIFT特征描述算子。然后通過判斷圖像是否具有匹配的局部描述算子來定義圖像之間的連接,實現圖像可視化連接,圖的邊代表連接。在實現圖像連接將會使用pydot工具包,該工具包是功能強大的GraphViz圖形庫的Python接口。
2.遇到的問題以及解決方法:
(1)環境變量沒有配置好  
      首先下載安裝graphviz-2.38.msi,再運行命令pip install pydot,最后可在系統路徑PATH中添加graphviz的路徑:C:\Program Files (x86)\Graphviz2.38\bin。(注意:graphviz安裝路徑可以隨便存。pydot的Node節點添加圖片時,圖片的路徑需要為絕對路徑,且分隔符為/ )
(2)報錯提示是沒有找到.png圖片,說明在dot.exe出了問題,去檢查環境變量
我原來是只在 用戶變量的PATH里添加的
方法:在系統變量里添加再添加路徑就行了

3.運行代碼:

# -*- coding: utf-8 -*-
from pylab import *
from PIL import Image
from PCV.localdescriptors import sift
from PCV.tools import imtools
import pydot
 
""" This is the example graph illustration of matching images from Figure 2-10.
To download the images, see ch2_download_panoramio.py."""
 
#download_path = "panoimages"  # set this to the path where you downloaded the panoramio images
#path = "/FULLPATH/panoimages/"  # path to save thumbnails (pydot needs the full system path)
 
#download_path = "F:\\dropbox\\Dropbox\\translation\\pcv-notebook\\data\\panoimages"  # set this to the path where you downloaded the panoramio images
#path = "F:\\dropbox\\Dropbox\\translation\\pcv-notebook\\data\\panoimages\\"  # path to save thumbnails (pydot needs the full system path)
download_path = "D:/new/test1"
path = "D:/new/test1"
# list of downloaded filenames
imlist = imtools.get_imlist(download_path)
nbr_images = len(imlist)
 
# extract features
featlist = [imname[:-3] + 'sift' for imname in imlist]
for i, imname in enumerate(imlist):
    sift.process_image(imname, featlist[i])
 
matchscores = zeros((nbr_images, nbr_images))
 
for i in range(nbr_images):
    for j in range(i, nbr_images):  # only compute upper triangle
        print 'comparing ', imlist[i], imlist[j]
        l1, d1 = sift.read_features_from_file(featlist[i])
        l2, d2 = sift.read_features_from_file(featlist[j])
        matches = sift.match_twosided(d1, d2)
        nbr_matches = sum(matches > 0)
        print 'number of matches = ', nbr_matches
        matchscores[i, j] = nbr_matches
print "The match scores is: %d", matchscores
 
#np.savetxt(("../data/panoimages/panoramio_matches.txt",matchscores)
 
# copy values
for i in range(nbr_images):
    for j in range(i + 1, nbr_images):  # no need to copy diagonal
        matchscores[j, i] = matchscores[i, j]
 
threshold = 2  # min number of matches needed to create link
 
g = pydot.Dot(graph_type='graph')  # don't want the default directed graph
 
for i in range(nbr_images):
    for j in range(i + 1, nbr_images):
        if matchscores[i, j] > threshold:
            # first image in pair
            im = Image.open(imlist[i])
            im.thumbnail((100, 100))
            filename = path + str(i) + '.png'
            im.save(filename)  # need temporary files of the right size
            g.add_node(pydot.Node(str(i), fontcolor='transparent', shape='rectangle', image=filename))
 
            # second image in pair
            im = Image.open(imlist[j])
            im.thumbnail((100, 100))
            filename = path + str(j) + '.png'
            im.save(filename)  # need temporary files of the right size
            g.add_node(pydot.Node(str(j), fontcolor='transparent', shape='rectangle', image=filename))
 
            g.add_edge(pydot.Edge(str(i), str(j)))
g.write_png('pig.png')

2.實驗效果:

3.實驗總結:

SIFT方法對於旋轉、尺度縮放、亮度變化保持不變性,而且對視角變化、仿射變換、噪聲也保持一定程度的穩定性,特征點的個數和有效點的比例沒有要求。當特征點不是很多時,經優化的SIFT匹配算法甚至可以達到實時的要求。而且可以很方便的與其他形式的特征向量進行聯合。特征點的個數和有效點的比例沒有要求。當特征點不是很多時,經優化的SIFT匹配算法甚至可以達到實時的要求。而且可以很方便的與其他形式的特征向量進行聯合,SIFT算法也有一些不足。方法通過對特征點構造128維的向量,然后對向量進行匹配,這樣圖像就得滿足足夠多的紋理,否則構造出的128維向量區別性就不是太大,容易造成誤匹配,極限情況如指紋圖像的匹配,星圖識別等這類圖像特征點周圍根本沒有什么紋理這時SIFT算法就完全失效了。在圖像標點地理匹配中,最開始用網上下載圖片執行不行,由於像素過高導致運行時間較長,需要調節圖片像素才行,當拍攝圖像特征點周圍紋理多且復雜,分類效果不佳。當拍攝圖像特征點周圍紋理相近,顏色相近,分類效果不佳。

 

實驗五:RANSAC算法

隨機抽樣一致算法(random sample consensus,RANSAC),采用迭代的方式從一組包含離群的被觀測數據中估算出數學模型的參數。

一、算法簡介:RANSAC算法的基本假設是樣本中包含正確數據(inliers,可以被模型描述的數據),也包含異常數據(outliers,偏離正常范圍很遠、無法適應數學模型的數據),即數據集中含有噪聲。這些異常數據可能是由於錯誤的測量、錯誤的假設、錯誤的計算等產生的。同時RANSAC也假設,給定一組正確的數據,存在可以計算出符合這些數據的模型參數的方法。

示例 :一個簡單的例子是從一組觀測數據中找出合適的2維直線。假設觀測數據中包含局內點和局外點,其中局內點近似的被直線所通過,而局外點遠離於直線。簡單的最小二乘法不能找到適應於局內點的直線,原因是最小二乘法盡量去適應包括局外點在內的所有點。相反,RANSAC能得出一個僅僅用局內點計算出模型,並且概率還足夠高。但是,RANSAC並不能保證結果一定正確,為了保證算法有足夠高的合理概率,我們必須小心的選擇算法的參數。

二、基本思想描述:

(1)對於一組觀測數據和一個用於解釋觀測數據的參數化模型, 所以滿足該模型的點為局內點,反之則為局外點。RANSAC算法的輸入是一組觀測數據(往往含有較大的噪聲或無效點),一個用於解釋觀測數據的參數化模型以及一些可信的參數。RANSAC通過反復選擇數據中的一組隨機子集來達成目標。被選取的子集被假設為局內點,並用下述方法進行驗證:

1、有一個模型適應於假設的局內點,即所有的未知參數都能從假設的局內點計算得出。

2、用1中得到的模型去測試所有的其它數據,如果某個點適用於估計的模型,認為它也是局內點。

3、如果有足夠多的點被歸類為假設的局內點,那么估計的模型就足夠合理。然后,用所有假設的局內點去重新估計模型(譬如使用最小二乘法),因為它僅僅被初始的假設局內點估計過。

4、最后,通過估計局內點與模型的錯誤率來評估模型。

上述過程被重復執行固定的次數,每次產生的模型要么因為局內點太少而被舍棄,要么因為比現有的模型更好而被選用。

(2)舉一個具體的例子, 比如利用RANSAC進行shift特征點匹配。

1、在我們的匹配點中隨機選擇4對點。這四對叫做局內點或者內點,而其他的匹配點叫做局外點或者外點

2、根據4對內點得到內部的單應性矩陣

3、使用這個計算出的單應性矩陣測試所有其他外點,通過一個閾值將所有的外點分為兩部分:

a.所有滿足這個單應性的外點被歸為是新的內點
b .而所有不滿足的外點被歸為新的外點

4、獲取我們所有的內點(新的內點+舊的內點)並轉到步驟2

5、只要沒有更改或我們已經迭代步驟2-步驟4 k次,結束迭代。
最終的單應矩陣將是我們想要的。

三、RANSAC算法步驟:

1. 隨機從數據集中隨機抽出4個樣本數據 (此4個樣本之間不能共線),計算出變換矩陣H,記為模型M;

2. 計算數據集中所有數據與模型M的投影誤差,若誤差小於閾值,加入內點集 I ;

3. 如果當前內點集 I 元素個數大於最優內點集 I_best , 則更新 I_best = I,同時更新迭代次數k ;

4. 如果迭代次數大於k,則退出 ; 否則迭代次數加1,並重復上述步驟;

注:迭代次數k在不大於最大迭代次數的情況下,是在不斷更新而不是固定的,見上方k的更新;
其中,p為置信度,一般取0.995;w為”內點”的比例 ; m為計算模型所需要的最少樣本數=4;
求得單應矩陣后就好辦了,把內點留下,內點就是篩選后的好用的點,外點舍棄,外點就有可能是誤匹配的點。

五、實驗內容:

(1)景深豐富場景:

匹配結果:

可以看到,這里面是由很多錯誤匹配的特征點的,這回導致配准后的結果出現兩幅圖片對不齊的結果,所以這里要做的是刪除這些錯誤的匹配點。RANSAC(Random Sample Consensus),它是根據一組包含異常數據的樣本數據集,計算出數據的數學模型參數,得到有效樣本數據的算法。具體可見:

代碼實現:

#include <iostream>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp> 
#include<opencv2/xfeatures2d.hpp>
#include<opencv2/core/core.hpp>
 
using namespace cv;  
using namespace std;
using namespace cv::xfeatures2d;//只有加上這句命名空間,SiftFeatureDetector and SiftFeatureExtractor才可以使用
 
int main()
{
	//Create SIFT class pointer
	Ptr<Feature2D> f2d = xfeatures2d::SIFT::create();
	//SiftFeatureDetector siftDetector;
	//Loading images
	Mat img_1 = imread("101200.jpg");
	Mat img_2 = imread("101201.jpg");
	if (!img_1.data || !img_2.data)
	{
		cout << "Reading picture error!" << endl;
		return false;
	}
	//Detect the keypoints
	double t0 = getTickCount();//當前滴答數
	vector<KeyPoint> keypoints_1, keypoints_2;
	f2d->detect(img_1, keypoints_1);
	f2d->detect(img_2, keypoints_2);
	cout << "The keypoints number of img1 is:" << keypoints_1.size() << endl;
	cout << "The keypoints number of img2 is:" << keypoints_2.size() << endl;
	//Calculate descriptors (feature vectors)
	Mat descriptors_1, descriptors_2;
	f2d->compute(img_1, keypoints_1, descriptors_1);
	f2d->compute(img_2, keypoints_2, descriptors_2);
	double freq = getTickFrequency();
	double tt = ((double)getTickCount() - t0) / freq;
	cout << "Extract SIFT Time:" <<tt<<"ms"<< endl;
	//畫關鍵點
	Mat img_keypoints_1, img_keypoints_2;
	drawKeypoints(img_1,keypoints_1,img_keypoints_1,Scalar::all(-1),0);
	drawKeypoints(img_2, keypoints_2, img_keypoints_2, Scalar::all(-1), 0);
	//imshow("img_keypoints_1",img_keypoints_1);
	//imshow("img_keypoints_2",img_keypoints_2);
 
	//Matching descriptor vector using BFMatcher
	BFMatcher matcher;
	vector<DMatch> matches;
	matcher.match(descriptors_1, descriptors_2, matches);
	cout << "The number of match:" << matches.size()<<endl;
	//繪制匹配出的關鍵點
	Mat img_matches;
	drawMatches(img_1, keypoints_1, img_2, keypoints_2, matches, img_matches);
	//imshow("Match image",img_matches);
	//計算匹配結果中距離最大和距離最小值
	double min_dist = matches[0].distance, max_dist = matches[0].distance;
	for (int m = 0; m < matches.size(); m++)
	{
		if (matches[m].distance<min_dist)
		{
			min_dist = matches[m].distance;
		}
		if (matches[m].distance>max_dist)
		{
			max_dist = matches[m].distance;
		}	
	}
	cout << "min dist=" << min_dist << endl;
	cout << "max dist=" << max_dist << endl;
	//篩選出較好的匹配點
	vector<DMatch> goodMatches;
	for (int m = 0; m < matches.size(); m++)
	{
		if (matches[m].distance < 0.6*max_dist)
		{
			goodMatches.push_back(matches[m]);
		}
	}
	cout << "The number of good matches:" <<goodMatches.size()<< endl;
	//畫出匹配結果
	Mat img_out;
	//紅色連接的是匹配的特征點數,綠色連接的是未匹配的特征點數
	//matchColor – Color of matches (lines and connected keypoints). If matchColor==Scalar::all(-1) , the color is generated randomly.
	//singlePointColor – Color of single keypoints(circles), which means that keypoints do not have the matches.If singlePointColor == Scalar::all(-1), the color is generated randomly.
	//CV_RGB(0, 255, 0)存儲順序為R-G-B,表示綠色
	drawMatches(img_1, keypoints_1, img_2, keypoints_2, goodMatches, img_out, Scalar::all(-1), CV_RGB(0, 0, 255), Mat(), 2);
	imshow("good Matches",img_out);
    //RANSAC匹配過程
	vector<DMatch> m_Matches;
	m_Matches = goodMatches;
	int ptCount = goodMatches.size();
	if (ptCount < 100)
	{
		cout << "Don't find enough match points" << endl;
		return 0;
	}
 
	//坐標轉換為float類型
	vector <KeyPoint> RAN_KP1, RAN_KP2;
	//size_t是標准C庫中定義的,應為unsigned int,在64位系統中為long unsigned int,在C++中為了適應不同的平台,增加可移植性。
	for (size_t i = 0; i < m_Matches.size(); i++)
	{
		RAN_KP1.push_back(keypoints_1[goodMatches[i].queryIdx]);
		RAN_KP2.push_back(keypoints_2[goodMatches[i].trainIdx]);
		//RAN_KP1是要存儲img01中能與img02匹配的點
		//goodMatches存儲了這些匹配點對的img01和img02的索引值
	}
	//坐標變換
	vector <Point2f> p01, p02;
	for (size_t i = 0; i < m_Matches.size(); i++)
	{
		p01.push_back(RAN_KP1[i].pt);
		p02.push_back(RAN_KP2[i].pt);
	}
	/*vector <Point2f> img1_corners(4);
	img1_corners[0] = Point(0,0);
	img1_corners[1] = Point(img_1.cols,0);
	img1_corners[2] = Point(img_1.cols, img_1.rows);
	img1_corners[3] = Point(0, img_1.rows);
	vector <Point2f> img2_corners(4);*/
	////求轉換矩陣
	//Mat m_homography;
	//vector<uchar> m;
	//m_homography = findHomography(p01, p02, RANSAC);//尋找匹配圖像
	//求基礎矩陣 Fundamental,3*3的基礎矩陣
	vector<uchar> RansacStatus;
	Mat Fundamental = findFundamentalMat(p01, p02, RansacStatus, FM_RANSAC);
	//重新定義關鍵點RR_KP和RR_matches來存儲新的關鍵點和基礎矩陣,通過RansacStatus來刪除誤匹配點
	vector <KeyPoint> RR_KP1, RR_KP2;
	vector <DMatch> RR_matches;
	int index = 0;
	for (size_t i = 0; i < m_Matches.size(); i++)
	{
		if (RansacStatus[i] != 0)
		{
			RR_KP1.push_back(RAN_KP1[i]);
			RR_KP2.push_back(RAN_KP2[i]);
			m_Matches[i].queryIdx = index;
			m_Matches[i].trainIdx = index;
			RR_matches.push_back(m_Matches[i]);
			index++;
		}
	}
	cout << "RANSAC后匹配點數" <<RR_matches.size()<< endl;
	Mat img_RR_matches;
	drawMatches(img_1, RR_KP1, img_2, RR_KP2, RR_matches, img_RR_matches);
	imshow("After RANSAC",img_RR_matches);
	//等待任意按鍵按下
	waitKey(0);
}

結果顯示:

很明顯圖中的大部分錯誤點被刪除掉。

結論:

發現經過處理之后的匹配結果,雖然依舊會有一點點微小的出入,但是匹配的精度卻極大地提高了。

1. RANSAC消除誤匹配點可以分為三部分:
(1)根據matches將特征點對齊,將坐標轉換為float類型

(2)使用求基礎矩陣的方法,findFundamentalMat,得到RansacStatus

(3)根據RansacStatus來刪除誤匹配點,即RansacStatus[0]=0的點。

2.函數說明

findFundamentalMat()得到基礎矩陣,基礎矩陣對應三維圖像中像點之間的對應關系。

findHomography()得到變換矩陣。

2. 優點與缺點

優點是對模型參數的估計具有魯棒性,能從具有大量局外點的觀測數據中估計出高精度的模型參數;

缺點就是迭代次數沒有上限,如果設置迭代次數很可能得不到最優的參數模型,甚至得到錯誤的模型。

RANSAC只有一定的概率得到可信模型,迭代次數越大,概率越高,兩者成正比。

RANSAC只能從數據集中得到一個模型,如果有多個模型,該算法不能實現。

RANSAC只有一定的概率得到的可信的模型,概率與迭代次數成正比。另一個缺點是它要求設置跟問題相關的閾值,

 

 

 



 

參考原文鏈接:https://blog.csdn.net/qq_27737701/article/details/82289607

                         https://blog.csdn.net/laobai1015/article/details/51682596

                         https://www.jianshu.com/p/bec33c7c333d

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM