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