計算機視覺 — SIFT特徵提取與檢索

一、SIFT算法介紹

1、介紹

SIFT,即尺度不變特徵變換(Scale-invariant feature transform,SIFT),是用於圖像處理領域的一種描述。這種描述具有尺度不變性,可在圖像中檢測出關鍵點,是一種局部特徵描述子。
SIFT特徵是基於物體上的一些局部外觀的興趣點而與影像的大小和旋轉無關。對於光線、噪聲、微視角改變的容忍度也相當高。基於這些特性,它們是高度顯著而且相對容易擷取,在母數龐大的特徵數據庫中,很容易辨識物體而且鮮有誤認。使用SIFT特徵描述對於部分物體遮蔽的偵測率也相當高,甚至只需要3個以上的SIFT物體特徵就足以計算出位置與方位。在現今的電腦硬件速度下和小型的特徵數據庫條件下,辨識速度可接近即時運算。SIFT特徵的信息量大,適合在海量數據庫中快速準確匹配。

2、實現步驟

SIFT特徵檢測主要包括以下4個基本步驟:

  1. 尺度空間極值檢測:
    搜索所有尺度上的圖像位置。通過高斯微分函數來識別潛在的對於尺度和旋轉不變的興趣點。
  2. 關鍵點定位
    在每個候選的位置上,通過一個擬合精細的模型來確定位置和尺度。關鍵點的選擇依據於它們的穩定程度。
  3. 方向確定
    基於圖像局部的梯度方向,分配給每個關鍵點位置一個或多個方向。所有後面的對圖像數據的操作都相對於關鍵點的方向、尺度和位置進行變換,從而提供對於這些變換的不變性。
  4. 關鍵點描述
    在每個關鍵點周圍的鄰域內,在選定的尺度上測量圖像局部的梯度。這些梯度被變換成一種表示,這種表示允許比較大的局部形狀的變形和光照變化。

3、 數學表達

SIFT算法是在不同的尺度空間上查找關鍵點,而尺度空間的獲取需要使用高斯模糊來實現,Lindeberg等人已證明高斯卷積核是實現尺度變換的唯一變換核,並且是唯一的線性核。
尺度空間使用高斯金字塔表示。Tony Lindeberg指出尺度規範化的LoG(Laplacion of Gaussian)算子具有真正的尺度不變性,Lowe使用高斯差分金字塔近似LoG算子,在尺度空間檢測穩定的關鍵點。
以上方法檢測到的極值點是離散空間的極值點,以下通過擬合三維二次函數來精確確定關鍵點的位置和尺度,同時去除低對比度的關鍵點和不穩定的邊緣響應點(因爲DoG算子會產生較強的邊緣響應),以增強匹配穩定性、提高抗噪聲能力。
爲了使描述符具有旋轉不變性,需要利用圖像的局部特徵爲給每一個關鍵點分配一個基準方向。使用圖像梯度的方法求取局部結構的穩定方向。對於在DOG金字塔中檢測出的關鍵點點,採集其所在高斯金字塔圖像3σ鄰域窗口內像素的梯度和方向分佈特徵。
通過以上步驟,對於每一個關鍵點,擁有三個信息:位置、尺度以及方向。接下來就是爲每個關鍵點建立一個描述符,用一組向量將這個關鍵點描述出來,使其不隨各種變化而改變,比如光照變化、視角變化等等。這個描述子不但包括關鍵點,也包含關鍵點周圍對其有貢獻的像素點,並且描述符應該有較高的獨特性,以便於提高特徵點正確匹配的概率。

SIFT描述子是關鍵點鄰域高斯圖像梯度統計結果的一種表示。通過對關鍵點周圍圖像區域分塊,計算塊內梯度直方圖,生成具有獨特性的向量,這個向量是該區域圖像信息的一種抽象,具有唯一性。

Lowe建議描述子使用在關鍵點尺度空間內4×4的窗口中計算的8個方向的梯度信息,共4×4×8=128維向量表徵。
可參考視覺小新https://blog.csdn.net/qq_15295565/article/details/83017919

二、SIFT特徵提取與檢索

1、特徵提取

圖片數據集:
在這裏插入圖片描述

  1. 結果展示(只截取了部分實驗結果)
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

  2. 小結
    將所有尺度上關鍵點提取的局部描述子取出來,就完成了圖像sift特徵的提取。

  3. 代碼實現

# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
from PCV.localdescriptors import sift
from PCV.localdescriptors import harris

# 添加中文字體支持
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)

imname = '../data/empire.jpg'
im = array(Image.open(imname).convert('L'))
sift.process_image(imname, 'empire.sift')
l1, d1 = sift.read_features_from_file('empire.sift')

figure()
gray()
subplot(131)
sift.plot_features(im, l1, circle=False)
title(u'SIFT特徵',fontproperties=font)
subplot(132)
sift.plot_features(im, l1, circle=True)
title(u'用圓圈表示SIFT特徵尺度',fontproperties=font)

# 檢測harris角點
harrisim = harris.compute_harris_response(im)

subplot(133)
filtered_coords = harris.get_harris_points(harrisim, 6, 0.1)
imshow(im)
plot([p[1] for p in filtered_coords], [p[0] for p in filtered_coords], '*')
axis('off')
title(u'Harris角點',fontproperties=font)

show()

2、特徵匹配

  1. 結果展示
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
  2. 小結
    在圖像匹配時,對兩幅圖像分別提取sift特徵,然後在每個尺度上進行描述子的匹配。在匹配中如果一個關鍵點的最小匹配距離和第二小匹配距離小於某個閾值,則認爲是正確匹配。
    sift算法表現出較好的目標定位能力,且能夠在一定程度上排除亮度差異、視角變化和複雜場景中其他物體的影響。這說明SIFT算法可以爲圖像目標對象檢索系統提供良好的圖像特徵。
  3. 代碼實現
from PIL import Image
from pylab import *
import sys
from PCV.localdescriptors import sift


if len(sys.argv) >= 3:
  im1f, im2f = sys.argv[1], sys.argv[2]
else:
#  im1f = '../data/sf_view1.jpg'
#  im2f = '../data/sf_view2.jpg'
  im1f = '../data/crans_1_small.jpg'
  im2f = '../data/crans_2_small.jpg'
#  im1f = '../data/climbing_1_small.jpg'
#  im2f = '../data/climbing_2_small.jpg'
im1 = array(Image.open(im1f))
im2 = array(Image.open(im2f))

sift.process_image(im1f, 'out_sift_1.txt')
l1, d1 = sift.read_features_from_file('out_sift_1.txt')
figure()
gray()
subplot(121)
sift.plot_features(im1, l1, circle=False)

sift.process_image(im2f, 'out_sift_2.txt')
l2, d2 = sift.read_features_from_file('out_sift_2.txt')
subplot(122)
sift.plot_features(im2, l2, circle=False)

#matches = sift.match(d1, d2)
matches = sift.match_twosided(d1, d2)
print '{} matches'.format(len(matches.nonzero()[0]))

figure()
gray()
sift.plot_matches(im1, im2, l1, l2, matches, show_below=True)
show()

3、數據集內部檢索匹配最多的圖片

原圖片:
在這裏插入圖片描述

  1. 結果展示
    匹配最多的3張圖片:
    在這裏插入圖片描述
  2. 代碼實現
# -*- coding: utf-8 -*-
from PIL import Image
from pylab import *
from numpy import *
import os
from PCV.tools.imtools import get_imlist  # 導入原書的PCV模塊
import matplotlib.pyplot as plt  # plt 用於顯示圖片
import matplotlib.image as mpimg  # mpimg 用於讀取圖片
 
 
def process_image(imagename, resultname, params="--edge-thresh 10 --peak-thresh 5"):
    """ 處理一幅圖像,然後將結果保存在文件中"""
    if imagename[-3:] != 'pgm':
        # 創建一個pgm文件
        im = Image.open(imagename).convert('L')
        im.save('tmp.pgm')
        imagename = 'tmp.pgm'
    cmmd = str(r"C:\Users\黃月浩\.spyder-py3\sift.exe " + imagename + " --output=" + resultname + " " + params)
    os.system(cmmd)
    print ('processed', imagename, 'to', resultname)
 
 
def read_features_from_file(filename):
    """讀取特徵屬性值,然後將其以矩陣的形式返回"""
    f = loadtxt(filename)
    return f[:, :4], f[:, 4:]  # 特徵位置,描述子
 
 
def write_featrues_to_file(filename, locs, desc):
    """將特徵位置和描述子保存到文件中"""
    savetxt(filename, hstack((locs, desc)))
 
 
def plot_features(im, locs, circle=False):
    """顯示帶有特徵的圖像
       輸入:im(數組圖像),locs(每個特徵的行、列、尺度和朝向)"""
 
    def draw_circle(c, r):
        t = arange(0, 1.01, .01) * 2 * pi
        x = r * cos(t) + c[0]
        y = r * sin(t) + c[1]
        plot(x, y, 'b', linewidth=2)
 
    imshow(im)
    if circle:
        for p in locs:
            draw_circle(p[:2], p[2])
    else:
        plot(locs[:, 0], locs[:, 1], 'ob')
    axis('off')
 
 
def match(desc1, desc2):
    """對於第一幅圖像中的每個描述子,選取其在第二幅圖像中的匹配
    輸入:desc1(第一幅圖像中的描述子),desc2(第二幅圖像中的描述子)"""
    desc1 = array([d / linalg.norm(d) for d in desc1])
    desc2 = array([d / linalg.norm(d) for d in desc2])
    dist_ratio = 0.6
    desc1_size = desc1.shape
    matchscores = zeros((desc1_size[0], 1), 'int')
    desc2t = desc2.T  # 預先計算矩陣轉置
    for i in range(desc1_size[0]):
        dotprods = dot(desc1[i, :], desc2t)  # 向量點乘
        dotprods = 0.9999 * dotprods
        # 反餘弦和反排序,返回第二幅圖像中特徵的索引
        indx = argsort(arccos(dotprods))
        # 檢查最近鄰的角度是否小於dist_ratio乘以第二近鄰的角度
        if arccos(dotprods)[indx[0]] < dist_ratio * arccos(dotprods)[indx[1]]:
            matchscores[i] = int(indx[0])
    return matchscores
 
 
def match_twosided(desc1, desc2):
    """雙向對稱版本的match()"""
    matches_12 = match(desc1, desc2)
    matches_21 = match(desc2, desc1)
    ndx_12 = matches_12.nonzero()[0]
    # 去除不對稱的匹配
    for n in ndx_12:
        if matches_21[int(matches_12[n])] != n:
            matches_12[n] = 0
    return matches_12
 
 
def appendimages(im1, im2):
    """返回將兩幅圖像並排拼接成的一幅新圖像"""
    # 選取具有最少行數的圖像,然後填充足夠的空行
    rows1 = im1.shape[0]
    rows2 = im2.shape[0]
    if rows1 < rows2:
        im1 = concatenate((im1, zeros((rows2 - rows1, im1.shape[1]))), axis=0)
    elif rows1 > rows2:
        im2 = concatenate((im2, zeros((rows1 - rows2, im2.shape[1]))), axis=0)
    return concatenate((im1, im2), axis=1)
 
 
def plot_matches(im1, im2, locs1, locs2, matchscores, show_below=True):
    """ 顯示一幅帶有連接匹配之間連線的圖片
        輸入:im1, im2(數組圖像), locs1,locs2(特徵位置),matchscores(match()的輸出),
        show_below(如果圖像應該顯示在匹配的下方)
    """
    im3 = appendimages(im1, im2)
    if show_below:
        im3 = vstack((im3, im3))
    imshow(im3)
    cols1 = im1.shape[1]
    for i in range(len(matchscores)):
        if matchscores[i] > 0:
            plot([locs1[i, 0], locs2[matchscores[i, 0], 0] + cols1], [locs1[i, 1], locs2[matchscores[i, 0], 1]], 'c')
    axis('off')
 
 
# 獲取project2_data文件夾下的圖片文件名(包括後綴名)
filelist = get_imlist('C:/Users/黃月浩/Desktop/pi')
 
# 輸入的圖片
im1f = 'C:/Users/黃月浩/Desktop/pi/6.jpg'
im1 = array(Image.open(im1f))
process_image(im1f, 'out_sift_1.txt')
l1, d1 = read_features_from_file('out_sift_1.txt')
 
i = 0
num = [0] * 30  # 存放匹配值
for infile in filelist:  # 對文件夾下的每張圖片進行如下操作
    im2 = array(Image.open(infile))
    process_image(infile, 'out_sift_2.txt')
    l2, d2 = read_features_from_file('out_sift_2.txt')
    matches = match_twosided(d1, d2)
    num[i] = len(matches.nonzero()[0])
    i = i + 1
    print ('{} matches'.format(num[i - 1]) ) # 輸出匹配值)
 
i = 1
figure()
while i < 4:  # 循環三次,輸出匹配最多的三張圖片
    index = num.index(max(num))
    print( index, filelist[index])
    lena = mpimg.imread(filelist[index])  # 讀取當前匹配最大值的圖片
    # 此時 lena 就已經是一個 np.array 了,可以對它進行任意處理
    # lena.shape  # (512, 512, 3)
    subplot(1, 3, i)
    plt.imshow(lena)  # 顯示圖片
    plt.axis('off')  # 不顯示座標軸
    num[index] = 0  # 將當前最大值清零
    i = i + 1
show()
 
 

4、地理標記圖像匹配(可視化接連的圖片)

  1. 結果展示
    在這裏插入圖片描述
    在這裏插入圖片描述
    第二次實驗數據集:
    在這裏插入圖片描述
  2. 小結
    對數據集中的圖片用地理標記圖像匹配,並可視化接連圖片。第一幅圖片都存在同樣的場景,匹配度越高,連線越多,其中最右邊最上面的第一幅圖片與其他圖片場景差別較大,所以連線較少,只有兩張圖片與他匹配。第二幅圖中,有兩張不同場景的圖片,但實驗結果中並沒有顯示,只有其他四張圖片有連線。
  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)

# 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: \n", 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('whitehouse.png')

5、RANSAC算法

  1. 算法簡介
    RANSAC可以用於圖片的拼接技術。在多幅圖像合成時,事先會在待合成的圖片中提取一些關鍵的特徵點。計算機視覺的研究表明,不同視角下物體往往可以通過一個透視矩陣(單應矩陣)(3X3或2X2)的變換而得到。RANSAC被用於擬合這個模型的參數(矩陣各行列的值),由此便可識別出不同照片中的同一物體。

    RANSAC主要解決樣本中的外點問題,最多可處理50%的外點情況。

    基本思想:

    RANSAC通過反覆選擇數據中的一組隨機子集來達成目標。被選取的子集被假設爲局內點,並用下述方法進行驗證:
    (1)有一個模型適用於假設的局內點,即所有的未知參數都能從假設的局內點計算得出。
    (2)用1中得到的模型去測試所有的其它數據,如果某個點適用於估計的模型,認爲它也是局內點。
    如果有足夠多的點被歸類爲假設的局內點,那麼估計的模型就足夠合理。
    (3)然後,用所有假設的局內點去重新估計模型,因爲它僅僅被初始的假設局內點估計過。
    (4) 最後,通過估計局內點與模型的錯誤率來評估模型。

    這個過程被重複執行固定的次數,每次產生的模型要麼因爲局內點太少而被捨棄,要麼因爲它比現有的模型更好而被選用。

    RANSAC算法經常用於計算機視覺,例如同時求解相關問題與估計立體攝像機的基礎矩陣,在圖像拼接時求變換矩陣的時候。

  2. 結果展示
    (1)景深單一,圖片較爲簡單

    sift特徵提取匹配點:在這裏插入圖片描述
    RANSAC算法剔除錯誤匹配點:
    在這裏插入圖片描述
    小結:景深單一,圖片較爲簡單的情況下,RANSAC算法相較於sift特徵提取有較好的匹配性能,刪除了許多錯誤的特徵匹配,如上圖中樹木的干擾,在sift特徵提取中實驗結果相對較差。

(2)景深豐富

sift特徵提取匹配點:
在這裏插入圖片描述

RANSAC算法剔除錯誤匹配點:在這裏插入圖片描述
小結:在景深豐富的情況下,sift特徵提取匹配點存在較多的錯誤匹配,而RANSAC算法較少了一些較爲明顯的錯誤匹配,但也仍然存在一些錯誤匹配。

(3)景深單一但圖片較爲複雜

sift特徵提取匹配點:
在這裏插入圖片描述
RANSAC算法剔除錯誤匹配點:
在這裏插入圖片描述
小結:在圖片較爲複雜的情況下,情況同景深豐富的情況有類似,sift特徵提取匹配點存在較多的錯誤匹配,而RANSAC算法較少了一些較爲明顯的錯誤匹配,只匹配出了較爲明顯的匹配點。

  1. 小結

    RANSAC的優點是它能魯棒的估計模型參數。例如,它能從包含大量局外點的數據集中估計出高精度的參數。RANSAC的缺點是它計算參數的迭代次數沒有上限;如果設置迭代次數的上限,得到的結果可能不是最優的結果,甚至可能得到錯誤的結果。RANSAC只有一定的概率得到可信的模型,概率與迭代次數成正比。RANSAC的另一個缺點是它要求設置跟問題相關的閥值。

    RANSAC只能從特定的數據集中估計出一個模型,如果存在兩個(或多個)模型,RANSAC不能找到別的模型。

  2. 代碼

# -*- coding: utf-8 -*-
import cv2

import numpy as np

import random



def compute_fundamental(x1, x2):

    n = x1.shape[1]

    if x2.shape[1] != n:

        raise ValueError("Number of points don't match.")



    # build matrix for equations

    A = np.zeros((n, 9))

    for i in range(n):

        A[i] = [x1[0, i] * x2[0, i], x1[0, i] * x2[1, i], x1[0, i] * x2[2, i],

                x1[1, i] * x2[0, i], x1[1, i] * x2[1, i], x1[1, i] * x2[2, i],

                x1[2, i] * x2[0, i], x1[2, i] * x2[1, i], x1[2, i] * x2[2, i]]



    # compute linear least square solution

    U, S, V = np.linalg.svd(A)

    F = V[-1].reshape(3, 3)



    # constrain F

    # make rank 2 by zeroing out last singular value

    U, S, V = np.linalg.svd(F)

    S[2] = 0

    F = np.dot(U, np.dot(np.diag(S), V))



    return F / F[2, 2]





def compute_fundamental_normalized(x1, x2):

    """    Computes the fundamental matrix from corresponding points

        (x1,x2 3*n arrays) using the normalized 8 point algorithm. """



    n = x1.shape[1]

    if x2.shape[1] != n:

        raise ValueError("Number of points don't match.")



    # normalize image coordinates

    x1 = x1 / x1[2]

    mean_1 = np.mean(x1[:2], axis=1)

    S1 = np.sqrt(2) / np.std(x1[:2])

    T1 = np.array([[S1, 0, -S1 * mean_1[0]], [0, S1, -S1 * mean_1[1]], [0, 0, 1]])

    x1 = np.dot(T1, x1)



    x2 = x2 / x2[2]

    mean_2 = np.mean(x2[:2], axis=1)

    S2 = np.sqrt(2) / np.std(x2[:2])

    T2 = np.array([[S2, 0, -S2 * mean_2[0]], [0, S2, -S2 * mean_2[1]], [0, 0, 1]])

    x2 = np.dot(T2, x2)



    # compute F with the normalized coordinates

    F = compute_fundamental(x1, x2)

    # print (F)

    # reverse normalization

    F = np.dot(T1.T, np.dot(F, T2))



    return F / F[2, 2]



def randSeed(good, num = 8):

    '''

    :param good: 初始的匹配點對

    :param num: 選擇隨機選取的點對數量

    :return: 8個點對list

    '''

    eight_point = random.sample(good, num)

    return eight_point



def PointCoordinates(eight_points, keypoints1, keypoints2):

    '''

    :param eight_points: 隨機八點

    :param keypoints1: 點座標

    :param keypoints2: 點座標

    :return:8個點

    '''

    x1 = []

    x2 = []

    tuple_dim = (1.,)

    for i in eight_points:

        tuple_x1 = keypoints1[i[0].queryIdx].pt + tuple_dim

        tuple_x2 = keypoints2[i[0].trainIdx].pt + tuple_dim

        x1.append(tuple_x1)

        x2.append(tuple_x2)

    return np.array(x1, dtype=float), np.array(x2, dtype=float)





def ransac(good, keypoints1, keypoints2, confidence,iter_num):

    Max_num = 0

    good_F = np.zeros([3,3])

    inlier_points = []

    for i in range(iter_num):

        eight_points = randSeed(good)

        x1,x2 = PointCoordinates(eight_points, keypoints1, keypoints2)

        F = compute_fundamental_normalized(x1.T, x2.T)

        num, ransac_good = inlier(F, good, keypoints1, keypoints2, confidence)

        if num > Max_num:

            Max_num = num

            good_F = F

            inlier_points = ransac_good

    print(Max_num, good_F)

    return Max_num, good_F, inlier_points





def computeReprojError(x1, x2, F):

    """

    計算投影誤差

    """

    ww = 1.0/(F[2,0]*x1[0]+F[2,1]*x1[1]+F[2,2])

    dx = (F[0,0]*x1[0]+F[0,1]*x1[1]+F[0,2])*ww - x2[0]

    dy = (F[1,0]*x1[0]+F[1,1]*x1[1]+F[1,2])*ww - x2[1]

    return dx*dx + dy*dy



def inlier(F,good, keypoints1,keypoints2,confidence):

    num = 0

    ransac_good = []

    x1, x2 = PointCoordinates(good, keypoints1, keypoints2)

    for i in range(len(x2)):

        line = F.dot(x1[i].T)

        #在對極幾何中極線表達式爲[A B C],Ax+By+C=0,  方向向量可以表示爲[-B,A]

        line_v = np.array([-line[1], line[0]])

        err = h = np.linalg.norm(np.cross(x2[i,:2], line_v)/np.linalg.norm(line_v))

        # err = computeReprojError(x1[i], x2[i], F)

        if abs(err) < confidence:

            ransac_good.append(good[i])

            num += 1

    return num, ransac_good





if __name__ =='__main__':

    #im1 =cv2.imread('D:/CVphoto/03063.jpg')
    #im2 = cv2.imread('D:/CVphoto/03065.jpg')
    im1 = r'C:\Users\Desktop\pi\1.jpg'
    im2 = r'C:\Users\Desktop\pi\2.jpg'



    print(cv2.__version__)

    psd_img_1 = cv2.imread(im1, cv2.IMREAD_COLOR)

    psd_img_2 = cv2.imread(im2, cv2.IMREAD_COLOR)

    # 3) SIFT特徵計算

    sift = cv2.xfeatures2d.SIFT_create()

    # find the keypoints and descriptors with SIFT

    kp1, des1 = sift.detectAndCompute(psd_img_1, None)

    kp2, des2 = sift.detectAndCompute(psd_img_2, None)



    # FLANN 參數設計

    match = cv2.BFMatcher()

    matches = match.knnMatch(des1, des2, k=2)



    # Apply ratio test

    # 比值測試,首先獲取與 A距離最近的點 B (最近)和 C (次近),

    # 只有當 B/C 小於閥值時(0.75)才被認爲是匹配,

    # 因爲假設匹配是一一對應的,真正的匹配的理想距離爲0

    good = []

    for m, n in matches:

        if m.distance < 0.75 * n.distance:

            good.append([m])

    print(good[0][0])



    print("number of feature points:",len(kp1), len(kp2))

    print(type(kp1[good[0][0].queryIdx].pt))

    print("good match num:{} good match points:".format(len(good)))

    for i in good:

        print(i[0].queryIdx, i[0].trainIdx)





    Max_num, good_F, inlier_points = ransac(good, kp1, kp2, confidence=30, iter_num=500)

    # cv2.drawMatchesKnn expects list of lists as matches.

    # img3 = np.ndarray([2, 2])

    # img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good[:10], img3, flags=2)



    # cv2.drawMatchesKnn expects list of lists as matches.



    img3 = cv2.drawMatchesKnn(psd_img_1,kp1,psd_img_2,kp2,good,None,flags=2)

    img4 = cv2.drawMatchesKnn(psd_img_1,kp1,psd_img_2,kp2,inlier_points,None,flags=2)

    cv2.namedWindow('image1', cv2.WINDOW_NORMAL)

    cv2.namedWindow('image2', cv2.WINDOW_NORMAL)

    cv2.imshow("image1",img3)

    cv2.imshow("image2",img4)

    cv2.waitKey(0)#等待按鍵按下

    cv2.destroyAllWindows()#清除所有窗口

三、總結

SIFT算法具有如下一些特點:

  1. SIFT特徵是圖像的局部特徵,其對旋轉、尺度縮放、亮度變化保持不變性,對視角變化、仿射變換、噪聲也保持一定程度的穩定性;
  2. 區分性(Distinctiveness)好,信息量豐富,適用於在海量特徵數據庫中進行快速、準確的匹配;
  3. 多量性,即使少數的幾個物體也可以產生大量的SIFT特徵向量;
  4. 高速性,經優化的SIFT匹配算法甚至可以達到實時的要求;
  5. 可擴展性,可以很方便的與其他形式的特徵向量進行聯合。

SIFT在圖像的不變特徵提取方面擁有無與倫比的優勢,但並不完美,仍然存在:

  1. 實時性不高。
  2. 有時特徵點較少。
  3. 對邊緣光滑的目標無法準確提取特徵點。

遇到的問題:

  1. empire.sift not found
    可參考Rolla_https://blog.csdn.net/weixin_42578378/article/details/88617349
  2. 實驗圖片注意需像素一致
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章