局部特徵匹配 Local Feature Matching
項目要求
- 實現興趣點檢測
- 實現類SIFT局部特徵描述
- 實現簡單匹配算法
項目原理
局部特徵
全局特徵(global features)指的是圖像的方差、顏色直方圖等等,描繪了圖像的整體信息,但是無法分辨出圖像中的前景和背景。
局部特徵(loacl features)指的是一些局部纔會出現的特徵,而且該部分需要滿足兩個條件:1. 能夠穩定出現; 2. 具有良好的可區分性。
當我們所關注的對象在圖像中受到部分遮擋,全局特徵可能會被破壞,但局部特徵依然能夠穩定存在,以代表這個物體。
圖像中的特徵點
一幅圖像中總存在着其獨特的像素點,這些點我們可以認爲就是這幅圖像的特徵,成爲特徵點。計算機視覺領域中的很重要的圖像特徵匹配就是一特徵點爲基礎而進行的,所以,如何定義和找出一幅圖像中的特徵點就非常重要。
在計算機視覺領域,興趣點(也稱關鍵點或特徵點)的概念已經得到了廣泛的應用, 包括目標識別、 圖像配準、 視覺跟蹤、 三維重建等。 這個概念的原理是, 從圖像中選取某些特徵點並對圖像進行局部分析,而非觀察整幅圖像。 只要圖像中有足夠多可檢測的興趣點,並且這些興趣點各不相同且特徵穩定, 能被精確地定位,上述方法就十分有效。
好的特徵應該有以下幾個特點:
- 重複性:不同圖像的相同區域應該能被重複檢測到,而且不受旋轉、模糊、光照等因素的影響。
- 可區分性:不同的檢測子應可通過檢測對應的描述子進行區分。
- 數量適宜:檢測子的數量過多或者過少都會降低識別精度。
- 高定位:檢測子的大小和位置均應固定。
- 有效性:具有較快的檢測速度。
Harris角點(Harris Corner Detector)
角點的基本原理:人眼對角點的識別通常是在一個局部的小區域或小窗口完成的。如果在各個方向上移動這個特徵的小窗口,窗口內區域的灰度發生了較大的變化,那麼就認爲在窗口內遇到了角點。如果這個特定的窗口在圖像各個方向上移動時,窗口內圖像的灰度沒有發生變化,那麼窗口內就不存在角點;如果窗口在某一個方向移動時,窗口內圖像的灰度發生了較大的變化,而在另一些方向上沒有發生變化,那麼,窗口內的圖像可能就是一條直線的線段。
角點具有一些良好的特徵:視角變化時也仍然可以很好的辨識;和周圍點在任何方向上變化都很大。角點經常被檢測在邊緣的交界處、被遮擋的邊緣、紋理性很強的部分。滿足這些條件一般都是穩定的、重複性比較高的點,所以實際上他們是不是角點並不重要(因爲我們的目標就是找一些穩定、重複性高的點以作爲特徵點)。
Harris Corner是最典型的角點檢測子,具有的優點是平移不變、旋轉不變,能克服一定光照變化。
NMS(非最大值抑制)
NMS(Non-Maximum Supression)非最大抑制,就是抑制除最大值(局部最優)外的其他元素。比如在下圖中要確認人行道上的車輛,需要找到區域內識別概率最大的框,所以將右邊0.9的框和左邊0.8的框高亮,之後遍歷其他邊框,如果和高亮的框交併比大於0.5,則將框進行淡化(抑制)。
SIFT(尺度不變特徵變換)
SIFT(Scale-Invariant Feature Transform)尺度不變特徵變換,是計算機視覺領域對圖像局部特徵進行描述的常用方法,有以下四個關鍵步驟
1. 尺度空間極值檢測(Scale-space extrema detection)
建立高斯差分金字塔,採用不同(尺度)的高斯覈對原圖像進行卷積操作,得到一組不同層的圖片,而不同組之間的圖片通過降採樣(隔點取點)得到。通過不斷的變化高斯核和進行降採樣,便可以得到高斯金字塔。再通過同一組不同兩層之間的圖片相減,就得到高斯差分金字塔(又叫尺度空間),如下圖所示。
其中,
組數,其中M和N是圖片的長和寬。
層數 ,其中n是希望提取從中特徵的圖片數目,因爲做差分的過程中有一張圖片無法使用,同時尺度空間上最上面一張和最下面一張圖片無法求導,所以S-3=n。
取法請見參考資料中原論文,不在此贅述。
理解:因爲SIFT要解決尺度不變性問題,它的理念是不僅在任何尺度下拍攝的物體都能檢測到一致的關鍵點,而且每個被檢測的特徵點都對應一個尺度因子。 在高斯金字塔中,其實降採樣就是模擬了近大遠小的尺寸變化,高斯核進行卷積模擬的是近處清晰,遠處模糊。(PS:值得注意的是,高斯核是唯一一個可以模擬近處清晰,遠處模糊的線性核)
2. 關鍵點定位(Keypoint Localization)
關鍵點需要滿足:包含很多信息,穩定不易變化。關鍵點通常是極值位置。
-> 1. 閾值化
,其中T=0.04,因爲如果太小了可能是噪聲,不進行保留。
-> 2. 在高斯差分金字塔中找極值
在考慮了尺度空間後,如果一個像素點的值比周圍26個像素點都大(小),那麼就認爲這個點是一個極值。
-> 3. 調整極值點的位置
因爲像素空間和尺度空間均是離散的,所有需要找到亞像素位置的精確極值點,具體操作是在檢測到的極值點處做三元二階泰勒展開,再對函數進行求導並令導數爲零。
-> 4. 捨去低對比度的點
若則認爲該像素點事噪聲,捨去點X
-> 5. 邊緣效應的去除
3. 方向賦值(Orientation assignment)
在最接近關鍵點尺度值的高斯圖像上,統計以特徵點爲圓心,以該特徵點所在的高斯圖像的尺度的1.5倍爲半徑的圓內所有像素的梯度方向以及其梯度幅值,並做的高斯濾波。
4. 關鍵點描述(Keypoint descriptor)
關鍵點的描述符是一個128維的向量,用k近臨算法進行向量匹配兩個圖片當中所有關鍵點的描述符中距離最近的兩個描述符,並進行連接。
在每一個子區域內統計八個方向上的梯度的經過高斯加權的長度,將每個子區域內的各個方向上的長度依次寫出,便得到描述符。
實驗步驟
(一) 讀取圖像,並對圖像進行預處理
簡介
讀取圖像,將圖片的長和寬放縮爲原來的1/2,再轉化爲灰度圖。
sepup_image()
def setup_image(img_name):
image1 = load_image('../data/'+img_name+'/'+img_name+'1.jpg')
image2 = load_image('../data/'+img_name+'/'+img_name+'2.jpg')
eval_file = '../data/'+img_name+'/'+img_name+'Eval.mat'
scale_factor = 0.5
image1 = cv2.resize(image1, (0, 0), fx=scale_factor, fy=scale_factor)
image2 = cv2.resize(image2, (0, 0), fx=scale_factor, fy=scale_factor)
image1_bw = cv2.cvtColor(image1, cv2.COLOR_RGB2GRAY)
image2_bw = cv2.cvtColor(image2, cv2.COLOR_RGB2GRAY)
return image1_bw,image2_bw
(二)使用Harris Corner Detector查找圖像中的角點
簡介
首先需要對圖片進行興趣點檢測,我使用Harris Corner Detector來檢查圖像中的角點,通過用矩陣的行列式減去矩陣的平方來確定特定像素得分,得分越高的像素越具有角的特徵,具體評判標準由閾值人爲設定。如果像素的角點得分高於閾值,便認爲它是一個角點,否則就忽略它。帶有角點標記的圖像如下面兩張圖所示。可見,檢測到大量的角點,且角點在圖像上的分佈不均勻。爲了保證角點分佈的均勻性,我採用了自適應的MNS來抑制多餘的角點。
get_interest_points()
def get_interest_points(image, feature_width):
alpha = 0.04
threshold = 10000
XCorners = []
YCorners = []
RValues = []
#Compute the size of the image.
ImageRows = image.shape[0]
ImageColumns = image.shape[1]
#Use the soble filter to calculate the x and y derivative of the image
Xderivative = cv2.Sobel(image, cv2.CV_64F,1,0,ksize=5)
Yderivative = cv2.Sobel(image, cv2.CV_64F,0,1,ksize=5)
#Define matrices Ixx, Iyy and Ixy
Ixx = (Xderivative)*(Xderivative)
Iyy = (Yderivative)*(Yderivative)
Ixy = (Xderivative)*(Yderivative)
#loop over the image to compute cornerness score of each pixel
for i in range(16, ImageRows - 16):
for j in range(16, ImageColumns - 16):
Ixx1 = Ixx[i-1:i+1, j-1:j+1]
Iyy1 = Iyy[i-1:i+1, j-1:j+1]
Ixy1 = Ixy[i-1:i+1, j-1:j+1]
Ixxsum = Ixx1.sum()
Iyysum = Iyy1.sum()
Ixysum = Ixy1.sum()
Determinant = Ixxsum*Iyysum - Ixysum**2
Trace = Ixxsum + Iyysum
R = Determinant - alpha*(Trace**2)
#Check if the cornerness score is above the threshold and if the pixel is an eligible corner pixel
if R > threshold:
XCorners.append(j)
YCorners.append(i)
RValues.append(R)
XCorners = np.asarray(XCorners)
YCorners = np.asarray(YCorners)
RValues = np.asarray(RValues)
#Use ANMS to evenly distribute the corners in the image.
NewCorners = ANMS(XCorners, YCorners, RValues, 3025)
NewCorners = np.asarray(NewCorners)
print(len(NewCorners))
#Return the x-y coordinates and cornerness score of the eligible corners.
x = NewCorners[:,0]
y = NewCorners[:,1]
scales = NewCorners[:,2]
return x,y, scales
輸出
下圖爲Harris Corner Detector對NotreDame圖像的角點檢測結果。
(三)使用ANMS進行角點篩選以獲得高分特徵點
簡介
使用Harris Corner Detector生成的角點不是均勻分佈的。在許多情況下角點可能集中在圖像的特定區域,從而導致準確性的下降。NMS算法通過在特徵函數中尋找局部最大值並丟棄剩餘的次大值(即圖像中的臨近角點)。通過使用該算法,可以提高特徵匹配的準確性,但是傳統的NMS具有某些侷限性,例如圖像中特徵點在處理後可能會不均勻分佈。爲了避免這種情況,我使用了ANMS算法(自適應非最大值抑制),基本思想是僅保留r個像素附近最大的那些點。下面兩張圖顯示了使用Harris角點檢測器生成的特徵點與抑制後剩餘的特徵點之間的比較。可見,圖像中的特徵點已從數7000個左右抑制到1500個,且均勻分佈。
ANMS()
def ANMS (x , y, r, maximum):
#x is an array of length N
#y is an array of length N
#r is the cornerness score
#max is the no of corners that are required
i = 0
j = 0
NewList = []
while i < len(x):
minimum = 1000000000000 #random large value
FirstCoordinate, SecondCoordinate = x[i], y[i]
while j < len(x):
CompareCoordinate1, CompareCoordinate2 = x[j], y[j]
if (FirstCoordinate != CompareCoordinate1 and SecondCoordinate != CompareCoordinate2) and r[i] < r[j]:
distance = math.sqrt((CompareCoordinate1 - FirstCoordinate)**2 + (CompareCoordinate2 - SecondCoordinate)**2)
if distance < minimum:
minimum = distance
j = j + 1
NewList.append([FirstCoordinate, SecondCoordinate, minimum])
i = i + 1
j = 0
NewList.sort(key = lambda t: t[2])
NewList = NewList[len(NewList)-maximum:len(NewList)]
print(NewList)
print(NewList.shape)
return NewList
輸出
下圖顯示了NotreDame圖像使用ANMS進行角點篩選後的特徵點。
(四)通過SIFT功能描述符進行關鍵點匹配
簡介
爲每個特徵點創建的特徵向量,用於將第一幅圖像的關鍵點與第二幅圖像的關鍵點進行匹配。爲此,計算第一圖像中每個關鍵點的每個特徵向量到第二圖像中每個特徵向量的距離。然後對距離進行排序,並獲取和比較兩個最小距離。爲了使關鍵點與第二個圖像中的另一個關鍵點精確匹配,計算並檢查兩個最小距離的比率是否大於指定的閾值,之後將其視爲關鍵點。
get_features()
功能
獲得給定興趣點集的一組特徵描述符。
參數
image:灰度圖
x:興趣點的x座標(np array)
y:興趣點的y座標(np array)
feature_width:局部特徵寬度(以像素爲單位)
返回值
fv:規範化特徵向量
源碼
def get_features(image, x, y, feature_width):
#Round off the x and y coordinates to integers.
x = np.rint(x)
x = x.astype(int)
y = np.rint(y)
y = y.astype(int)
#Define a gaussian filter.
cutoff_frequency = 10
filter1 = cv2.getGaussianKernel(ksize=4,sigma=cutoff_frequency)
filter1 = np.dot(filter1, filter1.T)
#Apply the gaussian filter to the image.
image = cv2.filter2D(image, -1, filter1)
ImageRows = image.shape[0]
ImageColumns = image.shape[1]
Xcoordinates = len(x)
Ycoordinates = len(y)
FeatureVectorIn = np.ones((Xcoordinates,128))
NormalizedFeature = np.zeros((Xcoordinates,128))
#loop over the corners generated by Harris
for i in range(Xcoordinates):
#Extract a 16X16 window centered at the corner pixel
temp1 = int(x[i])
temp2 = int(y[i])
Window = image[temp2-8:temp2 + 8, temp1-8:temp1 + 8]
WindowRows = Window.shape[0]
WindowColumns = Window.shape[1]
#loop over 16 4X4 windows and compute the magnitude and orientation of each pixel
for p in range(4):
for q in range(4):
WindowCut = Window[p*4:p*4 +4,q*4: q*4+4]
NewWindowCut = cv2.copyMakeBorder(WindowCut, 1, 1, 1, 1, cv2.BORDER_REFLECT)
Magnitude = np.zeros((4,4))
Orientation = np.zeros((4,4))
for r in range(WindowCut.shape[0]):
for s in range(WindowCut.shape[1]):
Magnitude[r,s] = math.sqrt((NewWindowCut[r+1,s] - NewWindowCut[r-1,s])**2 + (NewWindowCut[r,s+1] - NewWindowCut[r,s-1])**2)
Orientation[r,s] = np.arctan2((NewWindowCut[r+1,s] - NewWindowCut[r-1,s]),(NewWindowCut[r,s+1] - NewWindowCut[r,s-1]))
#put the generated orientation values to a histogram with the weights being the corresponding magnitude values
Magnitude = Magnitude
OrientationNew = Orientation*(180/(math.pi))
hist, edges = np.histogram(OrientationNew, bins = 8, range = (-180,180), weights = Magnitude)
for t in range(8):
l = t+p*32+q*8
FeatureVectorIn[i,l] = hist[t]
#Normalize the generated feature vector
for a in range(FeatureVectorIn.shape[0]):
sum1 = 0
for b in range(FeatureVectorIn.shape[1]):
sum1 = sum1 + (FeatureVectorIn[a][b])*(FeatureVectorIn[a][b])
sum1 = math.sqrt(sum1)
for c in range(FeatureVectorIn.shape[1]):
NormalizedFeature[a][c] = FeatureVectorIn[a][c]/sum1
#Return normalized feature vector
fv = NormalizedFeature
return fv
輸出
下圖顯示了NotreDame圖像對中前100個關鍵點匹配項。
實驗結果
load 標準配對數據文件和SIFT功能描述符的匹配結果進行比較,結果如下:
- NotreDame的配對準確率爲79%,如下圖所示
- MountRushmore的配對準確率爲32%,如下圖所示。
其他: - LaddObservatory
參考鏈接
Project 2: Local Feature Matching
CSCI 1430: Introduction to Computer Vision
特徵檢測和特徵匹配方法
局部特徵(1)——入門篇
Harris角點
C4W3L07 Nonmax Suppression
Distinctive Image Features from Scale-Invariant Keypoints-David G. Lowe