更多精彩內容請關注微信公衆號:聽潮庭。
計算機視覺的深度學習實戰四:圖像特徵提取
綜述:
- 顏色特徵
- 量化顏色直方圖、聚類顏色直方圖
- 幾何特徵
- Edge,Corner,Blob
- 基於關鍵點的特徵描述子
- SIFT、SURF、ORB
- 其他特徵提取:(LBP、Gabor)
- 代碼實踐
一、顏色特徵
1、量化顏色直方圖
- 適用顏色空間:RGB、HSV等顏色空間
- 操作
- 顏色空間量化,單元(bin)由單元中心代表
- 統計落在量化單元上的像素數量
- 最常用的方法是將顏色空間的各個分量(維度)均勻地進行劃分。
- HSV空間
- 優勢:計算高效
- 劣勢:量化問題、稀疏
2、聚類顏色直方圖
- 適用顏色空間:Lab等顏色空間
- 操作:
- 使用聚類算法對所有像素點顏色空間進行聚類
- 單元(bin)由聚類中心代表
- 聚類算法則考慮到圖像顏色特徵在整個空間的分佈情況,避免出現大量的bin中的像素數量非常稀疏的情況;
- lab空間是用數字化的方法來描述人的視覺感應。
- Lab顏色空間中:
- L分量用於表示像素的亮度,取值範圍是[1,100],表示從純黑到純白;
- a表示從品紅色到深綠色的範圍,取值範圍是[127,-128];
- b表示從黃色到藍色的範圍,取值範圍是[127,-128].
3、
- 設想兩幅圖像的顏色直方圖幾乎相同,只是互相錯開了一個bin,這時如果採用L1距離或者歐拉距離計算兩者的相似度,會得到很小的相似度值。
- 爲了克服這個缺陷,需要考慮到相似但不相同的顏色之間的相似度:
- 一種方法是採用二次式距離;
- 另一種方法是對顏色直方圖事先進行平滑過濾,即每個bin中的像素對於相鄰的幾個bin也有貢獻。
二、幾何特徵
1、邊緣(Edge)
- 像素明顯變化的區域
- 具有豐富的語義信息
- 用於
- 物體識別
- 幾何、視角變換
- 邊緣定義:
- 像素值函數快速變化的區域->一階導數的極值區域
- 像素值函數快速變化的區域->一階導數的極值區域
邊緣提取:
- 先高斯(高斯平滑)去噪,再用一階導數獲取極值
- 導數對噪聲敏感
- 高斯濾波yijiedao
- 標準差->尺度
- 梯度幅值/強度
- 梯度(增加最快)方向
- 邊緣提取尺度問題:
- 不同標準差的濾波(x方向)
- 能捕捉到不同尺度的邊緣
三、基於特徵點的特徵描述子
- 從不同的距離,不同的方向、角度,不同的光照條件下觀察一個物體時,物體的大小,形狀,敏感都會有所不同。但我們依然可以判斷它是同一物體。
- 理想的特徵描述子應該具備這些性質。即,在大小、方向、明暗不同的圖像中,同一特徵點應具有足夠相似的描述子,稱之爲描述子的可復現性。
1、幾何特徵:特徵點/關鍵點
- 不同視角圖片之間的映射
- 穩定局部特徵點
- 可重複性、顯著性
- 抗圖片變換:外貌變換(亮度,光照);幾何變換(平移、選擇、尺度)
興趣點/關鍵點(Interest point/Key point)
- 圖片配準/拼接
- 運動跟蹤
- 物體識別
- 機器人導航
- 3D重建
2、幾何特徵:Harris角點
Harris角點(Corner)
- 一種顯著點
- 在任何方向上移動小觀察窗,導致大的像素變動
- 在任何方向上移動小觀察窗,導致大的像素變動
- 數學模型:偏移(u,v)後窗內圖像變化
- 取 E(u,v)大的patch,其中w(x,y)相當於權重
- 取 E(u,v)大的patch,其中w(x,y)相當於權重
判斷Harris角點:
- 圖像中直線:一個特徵值大,另一個特徵值小;
- 圖像中平面:兩個特徵值都小,且近似相等;
- 圖像中角點:兩個特徵值都大,且近似相等;
3、FAST角點檢測
-
FAST角點檢測是一種快速角點特徵檢測算法。
-
FAST角點定義爲:若某像素點與其周圍領域內足夠多的像素點處於不同的區域,則該像素點可能爲角點,也就是某些屬性與衆不同。
-
FAST特徵點檢測是對興趣點所在圓周上的16個像素點進行判斷,若判斷後的當前中心像素點爲暗或亮,將決定其是否爲角點。
-
確定一個閾值t,觀察某像素點爲中心的一個半徑等於3像素的離散化的圓,這個圓的邊界上有16個像素。
-
如果在這個大小爲16個像素的圓上有N(12)個連續的像素點,他們的像素值要麼都比
大,要麼都比
小,則p他就是一個角點。
4、幾何特徵:斑點
- 拉普拉斯梯度
- 一階導極值點→二階導數零點
- 梯度/邊緣可以通過尋找二階導數接近零
- 但對噪聲很敏感,首先對圖像進行高斯卷積濾波進行降噪出路,再採用Laplace算子進行邊緣檢測。
- 高斯拉普拉斯濾波/Laplacian of Ganssian(LoG)
- 當sigma較小時,將識別出更爲細節的邊緣。
斑點(Blob)
- LoG圖找極值點→斑點
局部特徵:SIFT(尺度不變特徵變換)Scale-invariant feature transform
- 基於尺度空間不變的特徵
- SIFT特徵計算步驟
- 在DoG尺度空間中獲取極值點,即關鍵點
- LoG尺度空間和DoG(差分高斯)尺度空間
- 對關鍵點處理
- 位置插值(獲得精確的關鍵點)
- 去除邊緣點
- 關鍵點的方向估計
- 關鍵點描述子的生成
- 區域座標旋轉
- 計算採樣區域的直方圖
- 在DoG尺度空間中獲取極值點,即關鍵點
- 特點:
- 具有良好的不變性
- 旋轉、尺度縮放、平移、亮度變化、
- 對視角變化、仿射變換和噪聲也有一定程度的穩定性
- 獨特性好,信息量豐富
- 適用於在海量特徵數據庫中進行快速、準確的匹配
- 多量性
- 即使少數物體也可以產生大量的SIFT特徵
- 計算快
- 經優化的SIFT匹配算法甚至可以達到實時性
- 可擴展性,可以很方便的與其他形式的特徵向量進行聯合。
- 具有良好的不變性
- 尺度空間:
- 使用不同σ的LoG對圖片進行濾波
- 使用LoG,則後續計算量大,故使用DoG來代替LoG,用差分代替微分。
- 使用不同σ的LoG對圖片進行濾波
- 高斯金字塔就是在傳統金字塔的基礎上,對每一層用不同的參數σ做高斯模糊,是的每一層金字塔有多張高斯模糊圖像,這樣一組圖像是一個octave。
- SIFT-計算高斯差分(DoG)空間
- SIFt-DoG空間極值點就是“關鍵點”
- 圓半徑→特徵點尺度
- 圓心→特徵點座標
- 通過擬合三維二次函數來精確確定關鍵點的位置和尺度
- 離散空間的極值點並不是真正的極值點,上圖顯示了二維函數離散空間得到的極值點與連續空間極值點的差別。利用已知的離散空間點插值得到的連續空間極值點的方法叫做子像素插值。
- 同時去除低對比度的關鍵點和不穩定的邊緣相應點(因爲DoG算子會產生較強的邊緣響應),以增強匹配穩定性、提高抗噪聲能力。
- DoG算子會產生較強的邊緣響應,需要剔除不穩定的邊緣相應點。獲取特徵點處的Hessian矩陣,主曲率通過一個2×2的Hessian矩陣H求出
SIFT-特徵點方向估計
- 在尺度上計算梯度直方圖
- 8方向以特徵點爲中心、以3×1.5σ爲半徑
- 活取最高值方向爲關鍵點主方向
- 爲了匹配的穩定性,將超過最高值80%的方向稱爲輔方向
- 爲了保證特徵矢量具有旋轉不變性,需要以特徵點爲中心,將特徵點附近鄰域內的圖像旋轉一個方向角θ
- 即將原圖像x軸轉到與主方向相同的方向。
SIFT-計算特徵點描述子
- 在旋轉後的座標上採樣16×16的像素窗
- 在旋轉後的座標上採樣16×16的像素牀
- 4×4網格
- 8方向直方圖
- 總共128維
- SIFT的缺點是:計算太複雜,如果不借助硬件加速或專門的圖像處理器很難實現。
- 改進方式:Haar-like特徵:
- Haar-like特徵分爲:邊緣檢測、線性特徵、中心特徵和對角線特徵,這些特徵組合成特徵模板
- 特徵模板有白色和黑色兩種矩形,並定義該模板的特徵值爲白色矩形像素和減去黑色矩形像素和。
- Haar特徵值反映了圖像的灰度變化情況。
- Haar-like特徵的快速計算:積分圖
- 同一個像素如果被包含在不同的Haar-like的特徵模板中,會被重複計算多次;
- 積分圖是根據四個角點就能計算區域內的像素之和的方法。
5、局部特徵:SURF
SURF(Speed-Up Robust Features)算子是Herbert Bay等人在2006年提出的,它是對SIFT的改進,可將速度提高三倍。
SURF只要是把SIFT中的某些運算做了簡化。
- SURF把SIFT中的高斯二階微分的模板進行了簡化,使得卷積平滑操作僅需要轉換成加減運算。
- 在方向確定階段,在圓形區域計算x,y方向的haar小波響應,找到模最大的扇形方向。
- 爲了找出圖像中的特徵點,需要對原圖進行變換,變換圖就是原圖每個像素的Hessian矩陣行列式的近似值構成的。
- 求Hessian時要先高斯平滑,然後求二階導數,這對於離散的像素點而言,是用模板卷積形成的,這兩種操作合在一起用一個Haar模板代替就可以了。
- 小造型
- 爲了保證旋轉不變性,在SURF中,統計特徵點領域內的Haar小波特徵。
- 即以特徵點爲中心,計算半徑爲6s(s爲特徵點所在的尺度值)的鄰域內,統計60度扇形內所有點在x(水平)和y(垂直)方向的Haar小波響應總和。
- 然後60度扇形以一定間隔進行旋轉,最後將最大值那個扇形的方向作爲該特徵點的主方向。
- 在特徵點周圍取一個正方形框,框的邊長爲20s(是所檢測到該特徵點所在的尺度)。該框的方向,就是檢測出來的主方向。
- 最終,SURF的特徵點特徵向量的維度爲64維。
- 然後把該框分爲16個子區域,每個子區域統計25個像素的水平方向和垂直方向的Haar小波特徵,這裏的水平和垂直方向都是相對主方向而言的。
- 近似SIFT算法,實現快速版
- 先確定後選點,再找最大值
- Haar模板
- 加速三倍
- 亮度效果下效果好
- 模糊方面優於SIFT
- 尺度不變上不及SIFT
- 旋轉不變上差很多
6、ORB特徵描述
- SIFT和SURF計算複雜,難以用於實時性特徵檢測,更何況SIFT與SURF以前還是收費的
- ORB特徵基於FAST角點的特徵點檢測與BRIEF特徵描述技術
ORB的基本思路
- 它是對FAST角點與BRIEF特徵描述子的一種結合與改進
- FAST角點檢測的缺點是:
- 缺乏尺度不變性;
- 可以通告構建高斯金字塔,然後在每一層金字塔圖像上檢測角點,來實現尺度不變性;
- BRIEF的缺點是
- 缺乏旋轉不變性;
- 需要給BRIEF加上旋轉不變性
7、BRIEF
-
BRIEF需要先平滑圖片,然後在特徵點周圍選擇一個Patch,在這個Patch內通過一種選定的方法來挑選Nd個點對。
-
比較點對中兩點像素的大小,進行如下賦值
-
所有Nd個點對,都進行比較之間,我們就生成了一個Nd長的二進制串。
-
點對的生成方式(共有五種)
- 1、X,Y都服從在[-s/2,s/2]範圍內的均勻分佈,且相互獨立
- 2、X,Y都服從均值爲0,方差爲S²/25d的高斯分佈,且相互獨立,即X和Y都已原點爲中心,進行同方差的高斯分佈;
-
點對的位置一旦隨機選定,就不能再更改
ORB對BRIEF的改進
- ORB在計算BRIEF描述子時建立的座標系是以關鍵點爲圓心,以關鍵點和取點區域的形心(圓形)的連線爲X軸建立座標系
- 計算形心時,圓形區域上每個點的質量是其對應的像素值
四、LBP
LBP(局部二值模式)
- 將每個像素點與周圍點大小比較
- 半徑爲R的圓上,均勻採樣P個點
- 大小量化爲0或1
- 多個bit組成一個數,統計每個數的直方圖
- 爲解決旋轉不變性的問題:將LBP周圍的二進制碼(如11110001)按位旋轉,取二進制碼最小的值爲最終LBP值。
- 如:對於11110001情況,我們按位旋轉,得到11100011,11000111,10001111,0001111,00111110,01111100,11111000七個不同的二進制數,最小值爲00011111.
- 改進的LBP:
- 將3×3鄰域擴展到任意鄰域,並用圓形鄰域代替了正方向鄰域,這種LBP特徵叫做Extended LBP,也叫Circular LBP。
- 將3×3鄰域擴展到任意鄰域,並用圓形鄰域代替了正方向鄰域,這種LBP特徵叫做Extended LBP,也叫Circular LBP。
- LBP特徵具有灰度不變性和旋轉不變性等顯著優點。
五、Gabor
- Gabor是一個用於邊緣提取的線性濾波器,其頻率和方向表達與人類視覺系統類似,能夠提供良好的方向選擇和尺度選擇特性,而且對於光照變化不敏感;
- 十分適合紋理分析
- 使用一個三角函數與一個高斯函數疊加就得到了一個Gabor濾波器
Gabor濾波器組
-
Gabor濾波器組類似於人類的視覺系統
- 多頻率/尺度
- 多方向
-
Gabor濾波器
- 頻域:屬於加窗傅里葉變換
- 空域:一個高斯核函數和正弦平面波的乘積
-
三尺度
-
八方向
六、代碼實現
1、Haaris Corner
import numpy as np
import cv2 as cv
filename = "picture/chessboard.png"
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv.cornerHarris(gray,2,3,0.04)
#result is dilated for marking the corners, not important
dst = cv.dilate(dst,None)
# Threshold for an optimal value, it may vary depending on the image.
img[dst>0.01*dst.max()]=[0,0,255]
cv.imshow('dst',img)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
2、ORB
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
img1 = cv.imread('picture/box.png',0) # queryImage
img2 = cv.imread('picture/box_in_scene.png',0) # trainImage
# Initiate ORB detector
orb = cv.ORB_create()
# find the keypoints and descriptors with ORB
kp1, des1 = orb.detectAndCompute(img1,None)
kp2, des2 = orb.detectAndCompute(img2,None)
# create BFMatcher object
bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True)
# Match descriptors.
matches = bf.match(des1,des2)
# Sort them in the order of their distance.
matches = sorted(matches, key = lambda x:x.distance)
# Draw first 10 matches.
img3 = cv.drawMatches(img1,kp1,img2,kp2,matches[:20],None, flags=2)
plt.imshow(img3),plt.show()
3、圖片拼接
(注意原始圖片不能過大,否則報錯,此處500*375)
from Stitcher import Stitcher
import cv2
# 讀取拼接圖片
imageA = cv2.imread("image/3.png")
imageB = cv2.imread("image/4.png")
# 把圖片拼接成全景圖
stitcher = Stitcher()
(result, vis) = stitcher.stitch([imageA, imageB], showMatches=True)
# 顯示所有圖片
cv2.imshow("Image A", imageA)
cv2.imshow("Image B", imageB)
cv2.imshow("Keypoint Matches", vis)
cv2.imshow("Result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
import numpy as np
import cv2
class Stitcher:
#拼接函數
def stitch(self, images, ratio=0.75, reprojThresh=4.0,showMatches=False):
#獲取輸入圖片
(imageB, imageA) = images
#檢測A、B圖片的SIFT關鍵特徵點,並計算特徵描述子
(kpsA, featuresA) = self.detectAndDescribe(imageA)
(kpsB, featuresB) = self.detectAndDescribe(imageB)
# 匹配兩張圖片的所有特徵點,返回匹配結果
M = self.matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh)
# 如果返回結果爲空,沒有匹配成功的特徵點,退出算法
if M is None:
return None
# 否則,提取匹配結果
# H是3x3視角變換矩陣
(matches, H, status) = M
# 將圖片A進行視角變換,result是變換後圖片
result = cv2.warpPerspective(imageA, H, (imageA.shape[1] + imageB.shape[1], imageA.shape[0]))
# 將圖片B傳入result圖片最左端
result[0:imageB.shape[0], 0:imageB.shape[1]] = imageB
# 檢測是否需要顯示圖片匹配
if showMatches:
# 生成匹配圖片
vis = self.drawMatches(imageA, imageB, kpsA, kpsB, matches, status)
# 返回結果
return (result, vis)
# 返回匹配結果
return result
def detectAndDescribe(self, image):
# 將彩色圖片轉換成灰度圖
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 建立SIFT生成器
descriptor = cv2.xfeatures2d.SIFT_create()
# 檢測SIFT特徵點,並計算描述子
(kps, features) = descriptor.detectAndCompute(image, None)
# 將結果轉換成NumPy數組
kps = np.float32([kp.pt for kp in kps])
# 返回特徵點集,及對應的描述特徵
return (kps, features)
def matchKeypoints(self, kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh):
# 建立暴力匹配器
matcher = cv2.DescriptorMatcher_create("BruteForce")
# 使用KNN檢測來自A、B圖的SIFT特徵匹配對,K=2
rawMatches = matcher.knnMatch(featuresA, featuresB, 2)
matches = []
for m in rawMatches:
# 當最近距離跟次近距離的比值小於ratio值時,保留此匹配對
if len(m) == 2 and m[0].distance < m[1].distance * ratio:
# 存儲兩個點在featuresA, featuresB中的索引值
matches.append((m[0].trainIdx, m[0].queryIdx))
# 當篩選後的匹配對大於4時,計算視角變換矩陣
if len(matches) > 4:
# 獲取匹配對的點座標
ptsA = np.float32([kpsA[i] for (_, i) in matches])
ptsB = np.float32([kpsB[i] for (i, _) in matches])
# 計算視角變換矩陣
(H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reprojThresh)
# 返回結果
return (matches, H, status)
# 如果匹配對小於4時,返回None
return None
def drawMatches(self, imageA, imageB, kpsA, kpsB, matches, status):
# 初始化可視化圖片,將A、B圖左右連接到一起
(hA, wA) = imageA.shape[:2]
(hB, wB) = imageB.shape[:2]
vis = np.zeros((max(hA, hB), wA + wB, 3), dtype="uint8")
vis[0:hA, 0:wA] = imageA
vis[0:hB, wA:] = imageB
# 聯合遍歷,畫出匹配對
for ((trainIdx, queryIdx), s) in zip(matches, status):
# 當點對匹配成功時,畫到可視化圖上
if s == 1:
# 畫出匹配對
ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1]))
ptB = (int(kpsB[trainIdx][0]) + wA, int(kpsB[trainIdx][1]))
cv2.line(vis, ptA, ptB, (0, 255, 0), 1)
# 返回可視化結果
return vis