上一篇介紹了 High-pass Filter。
我們透過保留高頻資訊,讓圖片中的邊緣變得更加明顯。
那現在請大家思考一個問題:
如果現在有兩張照片,其中一張只是同一個物體稍微平移了一點,而另一張則是從不同角度拍攝。
電腦要怎麼知道它們其實是同一個物體?
如果只是利用邊緣好像還不太夠,因為一條直線上的每個 Pixel 看起來都很相似,很難知道自己到底位在哪裡。
所以,電腦需要的是一種更有辨識度的特徵。
而最經典的方法之一,就是今天要介紹的 Harris Corner Detection。
邊緣雖然可以告訴我們物體的輪廓。
但是,如果沿著邊緣移動一小段距離,看起來其實沒有太大的差別。
換句話說,邊緣很容易產生模糊的定位,但是角點不同。
角點通常是兩條或多條邊緣相交的地方,只要位置稍微改變,周圍的亮度分布就會有明顯變化。
因此,比起邊緣,角點更容易成為圖片中的「地標」。
Harris Corner 的想法其實不難。
它會觀察一個小區域,然後試著往不同方向稍微移動。
接著比較移動前後的變化有多大。
大致可以分成三種情況:
第一種是平坦區域,不管往哪個方向移動,畫面幾乎都沒有改變。
第二種是邊緣,如果沿著邊緣移動,變化很小,但是垂直於邊緣移動,變化就會很大。
第三種就是角點,不管往哪一個方向移動,亮度都會明顯改變。
因此,Harris 就會判斷這裡是一個 Corner。
實際上,Harris 並不是直接找直角,而是分析一小塊區域內亮度變化的方向。
如果只有一個方向變化很明顯,通常代表這裡是一條邊。
如果所有方向的變化都很小,就是平坦區域。
而如果每個方向都有明顯變化,就代表這裡很可能是一個角點。
所以 Corner 不一定真的長得像 90 度。
只要周圍亮度在不同方向都有足夠變化,都可能被偵測出來。
OpenCV 已經提供了 cornerHarris()。
今天,我們不用前面用的狗狗照片,改成使用棋盤圖片,方便大家觀察。

import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("chessboard.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
corner = cv2.cornerHarris(
gray,
blockSize=2,
ksize=3,
k=0.04
)
corner = cv2.dilate(corner, None)
result = img.copy()
result[corner > 0.01 * corner.max()] = [0, 0, 255]
接著把結果畫出來。
plt.figure(figsize=(10,4))
plt.subplot(1,2,1)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title("Original")
plt.axis("off")
plt.subplot(1,2,2)
plt.imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
plt.title("Harris Corner")
plt.axis("off")
plt.tight_layout()
plt.show()
結果如下:

可以發現,棋盤格交會的位置,都被標示成紅色。
而長長的直線中間,則幾乎沒有被偵測出來。
這正是 Harris Corner 的特色,它關心的是角點,而不是整條邊緣。
Corner Detection 是許多 Computer Vision 演算法的重要基礎。
例如:
原因就在於,角點通常具有較高的辨識度。
即使圖片旋轉、縮放,甚至換了一個拍攝角度。
這些角點仍然比較容易被再次找到。
今天介紹了 Harris Corner Detection。
它找的是那些在不同方向都具有明顯亮度變化的角點。
和 Edge Detection 相比,Corner 能提供更穩定的位置資訊,因此在許多 Computer Vision 應用中,都被當作重要的特徵點。
不過,Harris Corner 只能告訴我們「哪裡有角點」,如果今天希望電腦不只能找到角點,還能替每個角點建立一份 id,讓不同圖片中的相同位置也能互相對應,又該怎麼做?
下一篇,我們就來介紹 SIFT 與 ORB,看看現代特徵點偵測與描述是如何做到這件事。