角檢測(Corner detection)是指檢測圖像中具備表明性的(咱們感興趣的)角點,通常講爲形狀或邊緣的拐角處,這些點能夠大略標記對象在圖像中的輪廓和位置,若是從一個圖像序列中檢測每一個圖像的角點,就能夠找出圖像之間存在的相關和相對應的角點,這對好比全景拼接(多張圖片拼接成一張全景圖片)頗有用。
角檢測還能夠用在運動檢測、物體識別等方面。html
Harris角檢測(也叫Harris & Stephens角檢測)是目前可用的最簡單的角檢測算法。它的基本思路是這樣的:對於圖像中的一個點,若是它周圍存在1個以上不一樣方向的邊緣,這個點所在處就是角。python
下面須要粗略地介紹一下其中的數學原理,以便理解Harris濾波器函數參數的做用。算法
咱們以前學習邊緣檢測的時候知道,邊緣上的點,水平和垂直兩個方向的梯度幅度(一階導數)較周圍高,若是要檢測點所在處是否具備1個方向以上的邊緣,就必需要綜合它周圍點的梯度一塊兒考慮,那麼問題就變成了須要計算周圍區域,像素兩兩之間的梯度關係,咱們在學習PCA算法的時候知道協方差矩陣能體現這種關係,設Ix爲點x在它周圍一小塊區域內的水平方向梯度,一樣,設Iy爲點y垂直方向梯度,組成一個協方差矩陣:segmentfault
在點(x,y)附近一小塊區域內,離(x,y)越近,關係越大,這就須要考慮加權計算,設加權算子爲W(典型值使用高斯核,以前筆記介紹過),獲得:
A = W * M數組
A被稱爲Harris矩陣,它兩個特徵值λ1和λ2,若是:函數
λ1和λ2都爲較大的正數,表示對應的(x,y)點處是角學習
若λ1較大,並且λ2約等於0,表示所在點只有一條邊,非角測試
若λ1和λ2都約等於0,表示所在處沒有邊角spa
求Harris矩陣的特徵值計算量較大,Harris給出了一個方程:code
上式只要計算矩陣的行列式(det)和跡(trace)便可,計算方便,獲得的結果可做爲角的檢測,其中係數k是一個經驗值,它的設置跟邊緣的粗細有關。咱們暫且把這種方法稱爲k方法。
爲使得計算更方便,Noble角測量(Noble’s corner measure)給出了去除k係數的方法,只要計算:
eps(或ϵ)爲一個很小的正的常量,咱們暫且稱此爲eps方法。
Harris代碼實現
根據以上所介紹的eps方法,下面實現一個Harris角檢測函數:
def harris_eps(im, sigma=3): imx = np.zeros(im.shape) filters.gaussian_filter(im, (sigma,sigma), (0,1), imx) imy = np.zeros(im.shape) filters.gaussian_filter(im, (sigma,sigma), (1,0), imy) #計算兩兩之間的一階導數 Wxx = filters.gaussian_filter(imx*imx,sigma) Wxy = filters.gaussian_filter(imx*imy,sigma) Wyy = filters.gaussian_filter(imy*imy,sigma) #計算行列式 Wdet = Wxx*Wyy - Wxy**2 #計算矩陣的跡 Wtr = Wxx + Wyy #按eps公式計算 return Wdet * 2 / (Wtr + 1e-06)
注意:書上並無嚴格按照公式計算返回值,經測試,對某些圖片會出現沒法除的狀況,因此上面的代碼進行了改正
肯定座標
Harris返回的結果是一個與原圖像大小相同的矩陣,要判斷是不是角點,還須要作以下的工做:
設定一個閾值,只考慮高於閾值的點,這樣能夠過濾掉無用的或不感興趣的點
一個角處通常會有多個點,在標記角座標的時候,應該設定一個最小距離,在此距離內只須要一個點進行標記便可
這個判斷函數可使用skimage庫中的corner_peaks函數,其中參數min_distance指上述的最小距離,threshold_rel則爲閾值,函數原型:
skimage.feature.corner_peaks(harrisim, min_distance=10, threshold_abs=0, threshold_rel=0.1, ...)
函數默認返回由全部角點在原圖像中的座標組成的數組。
skimage庫也提供了Harris角檢測函數:
skimage.feature.corner_harris(image, method='k', k=0.05, eps=1e-06, sigma=1) method: 'k'或'eps',對應上述的兩種計算方法 k: k方法中的k係數,取值區間爲[0, 0.2],k的值越小,表示將檢測越銳利的角 eps: eps方法中的係數,默認便可 sigma: 高斯核的標準差
簡單示例:
import numpy as np from skimage.feature import corner_harris, corner_peaks square = np.zeros([10, 10]) square[2:8, 2:8] = 1 square.astype(int) print square >>[[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [ 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.] [ 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.] [ 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.] [ 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.] [ 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.] [ 0. 0. 1. 1. 1. 1. 1. 1. 0. 0.] [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]] harris_result = corner_harris(square) print corner_peaks(harris_result, min_distance=1) #此函數可以從harris結果中檢測角的座標位置 >>[[2 2] [2 7] [7 2] [7 7]]
上面harris_result如圖,觀察一下角處的值與周圍的不一樣:
我分別用咱們本身實現的harris_eps函數,跟skimage中的corner_harris函數進行效果對比,發現二者存在差別,有使用了兩張圖像進行了測試,一張是內容比較簡單的矢量圖,一張是寫實圖,效果以下:
能夠看到,使用簡單的房子的圖像時,經過微調參數,三種方法均可以達到比較接近的效果。但使用寫實的圖像(第二列)時,三者差別較大,skiamge庫的版本檢測出的角結果不是咱們指望的。並且我經過調整參數也很難達到效果。緣由還不清楚,有空再回頭分析一下corner_harris函數的源代碼。
以上示例的代碼:
from PIL import Image import matplotlib.pyplot as plt import numpy as np from skimage.feature import corner_harris, corner_peaks from scipy.ndimage import filters #harris_eps函數此處省略,見上文 im1 = np.array(Image.open('house.jpg').convert('L')) im2 = np.array(Image.open('tower-left.jpg').convert('L')) my_coords1 = corner_peaks(harris_eps(im1, sigma=1), min_distance=12, threshold_rel=0) eps_coords1 = corner_peaks(corner_harris(im1, method='eps', sigma=1), min_distance=20, threshold_rel=0) k_coords1 = corner_peaks(corner_harris(im1, method='k', sigma=1), min_distance=20, threshold_rel=0) my_coords2 = corner_peaks(harris_eps(im2, sigma=1), min_distance=5, threshold_rel=0.01) eps_coords2 = corner_peaks(corner_harris(im2, method='eps', sigma=1), min_distance=5, threshold_rel=0.01) k_coords2 = corner_peaks(corner_harris(im2, method='k', sigma=1), min_distance=5, threshold_rel=0.01) def plot_coords(index, title, im, coords): plt.subplot(index) plt.imshow(im) plt.plot(coords[:, 1], coords[:, 0], '+r', markersize=5) plt.title(title) plt.axis('off') plt.gray() index = 321 plot_coords(index, 'my', im1, my_coords1) plot_coords(index + 1, 'my', im2, my_coords2) plot_coords(index + 2, 'skimage-eps', im1, eps_coords1) plot_coords(index + 3, 'skimage-eps', im2, eps_coords2) plot_coords(index + 4, 'skimage-k', im1, k_coords1) plot_coords(index + 5, 'skimage-k', im2, k_coords2) plt.tight_layout(w_pad=0) plt.show()
下一筆記學習如何從圖像間找出相關的對應點。
你還能夠查看個人其它筆記