建到圖像匹配實戰(zhàn))
幾年前做圖像拼接項目時甲方給的素材基本都是手機(jī)隨手拍的照片角度、光照、遮擋五花八門傳統(tǒng)的角點檢測換了好幾種匹配結(jié)果只能用“慘不忍睹”來形容。后來狠下心把SIFT整套流程啃了一遍問題才算真正解決。SIFT確實是目前公認(rèn)的、在尺度變化、旋轉(zhuǎn)、光照變化下表現(xiàn)最穩(wěn)定的局部特征描述子雖然它已經(jīng)問世二十多年但在很多實際工程里依然是首選。這篇文章就把我對SIFT的完整理解和落地經(jīng)驗做個梳理從原理到實操從參數(shù)調(diào)試到坑點排查全程按照自己踩過的路子來寫希望能幫到正在做圖像匹配、三維重建、物體識別或者全景拼接的朋友。1. SIFT為什么能被稱為“最佳性能描述子”先聊一個最直接的問題市面上特征點算法那么多Harris、FAST、ORB、SURF、BRISK、AKAZE一個比一個快為什么在需要穩(wěn)定匹配的場景里大家還是會回頭選SIFTSIFT全稱是Scale-Invariant Feature Transform尺度不變特征變換。它最核心的價值是兩個第一提取出的特征點對圖像的縮放、旋轉(zhuǎn)、光照變化有很強(qiáng)的魯棒性第二它生成的描述子有128維信息量足夠大區(qū)分能力強(qiáng)在遮擋、視角變化的情況下依然能保持較好的匹配效果。這兩個特性恰好是很多實際視覺任務(wù)的基礎(chǔ)。拿FAST和ORB來說它們的速度優(yōu)勢是實打?qū)嵉牡秉c也很明顯——沒有真正的尺度空間分析遇到圖像縮放就很容易丟失特征點。舉個簡單的例子一張640×480的圖片檢測到500個角點把圖片縮小一半再檢測能重復(fù)命中的可能不到一半。這種尺度不穩(wěn)定性在圖像拼接、相機(jī)標(biāo)定、視覺定位等場景里是致命的因為同一場景在不同距離、不同分辨率下出現(xiàn)是很正常的事。SIFT的另一大優(yōu)勢是它的描述子設(shè)計。它不只是在特征點周圍取一個固定大小的像素塊而是基于特征點的梯度方向分布構(gòu)建一個128維的向量。這個向量經(jīng)過歸一化處理后對光照變化有很強(qiáng)的抵抗力。實測下來即便兩張圖片的曝光差異很大SIFT描述子的歐氏距離依然能保持在合理范圍內(nèi)而很多輕量級描述子在這種條件下早就亂成一鍋粥了。當(dāng)然SIFT不是沒有代價。它的計算量明顯高于ORB這類算法在CPU上處理一張普通分辨率圖片可能需要幾十毫秒到上百毫秒。但換個角度想如果匹配失敗導(dǎo)致整個流程重來浪費(fèi)的時間遠(yuǎn)比多花的那幾十毫秒多得多。工程上追求的是整體鏈路的穩(wěn)定性而不是單個環(huán)節(jié)的最快速度。所以我的看法很明確SIFT在“性能”這個詞的完整含義——準(zhǔn)確性、魯棒性、可重復(fù)性——上確實配得上“最佳描述子”的稱號。它并非在所有場景都適合但在你能接受它的計算開銷的前提下它幾乎是最不會讓你失望的選擇。2. SIFT核心原理拆解尺度空間、關(guān)鍵點定位、方向分配與描述子構(gòu)建SIFT的整體流程可以分成四步構(gòu)建尺度空間、精確定位關(guān)鍵點、分配主方向、生成描述子。每一環(huán)都有自己的設(shè)計邏輯理解這些邏輯比單純背步驟重要得多因為你在調(diào)參和排查問題的時候只有理解了每一步在做什么才知道從哪個方向下手。2.1 尺度空間與高斯差分金字塔為什么非要搞一堆模糊圖SIFT的第一件事是構(gòu)建圖像的尺度空間。這個叫法聽起來玄乎說白了就是模擬人眼在不同距離觀察物體的效果。距離遠(yuǎn)的時候物體細(xì)節(jié)模糊、輪廓為主距離近的時候細(xì)節(jié)清晰。算法要做的就是生成一系列不同模糊程度、不同分辨率的圖像然后在里面找“在多個尺度下都穩(wěn)定存在”的特征點。具體做法是對原圖做高斯模糊用不同的標(biāo)準(zhǔn)差σ生成一組圖像這就是一個“八度”O(jiān)ctave。然后對圖像做隔點采樣縮小一半再生成下一組模糊圖如此重復(fù)。構(gòu)建好金字塔后用相鄰兩層高斯模糊圖做差得到高斯差分圖Difference of Gaussian簡稱DoG。這里有一個重要的參數(shù)高斯模糊的組數(shù)nOctaves和每組的層數(shù)nOctaveLayers。OpenCV里SIFT_create()默認(rèn)參數(shù)是nfeatures0、nOctaveLayers3、contrastThreshold0.04、edgeThreshold10、sigma1.6。如果你直接拿默認(rèn)參數(shù)干活多數(shù)情況下沒什么問題但理解這些參數(shù)背后的作用能讓你在特殊場景下精準(zhǔn)調(diào)整。比如做無人機(jī)航拍圖的拼接圖像紋理豐富但尺度跨度大適當(dāng)增加nOctaveLayers可以提升檢測穩(wěn)定性。DoG圖像的極值點檢測是找關(guān)鍵點的第一道篩子。算法會把每個像素和它同一尺度的8個鄰居、上下兩層各9個鄰居做一個比較一共比較26個點。只有比這些點都大或者都小才暫時算一個極值點候選。這個過程保證了特征點在尺度上具有局部唯一性。2.2 關(guān)鍵點精確定位剔除低對比度和邊緣點DoG檢測出的極值點只是粗略候選精度還不夠。因為DoG響應(yīng)在邊緣附近會產(chǎn)生較強(qiáng)的響應(yīng)圖像中一些細(xì)長結(jié)構(gòu)的邊緣點很容易被誤判成角點而這些點在真正的“角”上并不穩(wěn)定。所以SIFT做了兩件事來凈化候選點。第一件事是低對比度剔除。算法對DoG函數(shù)做泰勒展開求解極值點的精確位置偏移如果計算出的對比度絕對值太小就認(rèn)為這個點不夠“突出”直接丟棄。這個閾值由contrastThreshold參數(shù)控制默認(rèn)0.04。如果圖片整體對比度偏低閾值需要適當(dāng)下調(diào)比如0.02如果圖片噪聲大閾值可以適當(dāng)上調(diào)避免把噪聲點當(dāng)成特征點。第二件事是邊緣響應(yīng)剔除。這一步利用Hessian矩陣的特征值比例來判斷如果主曲率比例過大說明該點所在區(qū)域在某個方向上變化劇烈、另一個方向上幾乎不變典型的邊緣特征。默認(rèn)edgeThreshold10意思是允許的最大主曲率比為10。這個值越大保留的候選點越多但邊緣點也會混進(jìn)來越小則過濾越嚴(yán)格可能會丟掉一部分有效角點。我實際測試過很多次edgeThreshold保持默認(rèn)10就挺好除非你是做線特征相關(guān)的特殊任務(wù)否則不建議大改。2.3 方向分配旋轉(zhuǎn)不變性的來源SIFT能應(yīng)對旋轉(zhuǎn)變化靠的就是方向分配這一步。算法會以特征點為中心取一個鄰域窗口半徑通常是1.5倍的特征尺度計算每個像素的梯度幅值和梯度方向然后按方向統(tǒng)計直方圖。直方圖一共36個柱每個柱代表10度覆蓋360度。方向直方圖的峰值就是該特征點的主方向。為了保證平滑性還會對直方圖做一次平滑處理。如果存在另一個方向的峰值幅值達(dá)到主峰值的80%以上那么這個特征點會被復(fù)制成兩個點分別賦予兩個主方向。這個設(shè)計的意圖很清晰有些特征點本身就存在多個顯著方向如果只保留一個旋轉(zhuǎn)后可能匹配不上復(fù)制成兩個能提高匹配的穩(wěn)定性但也增加了后續(xù)匹配的計算量。這里有個小細(xì)節(jié)官方實現(xiàn)里直方圖的峰值是記錄在36個bin上的而不是8個bin。很多博客寫的“8個bin每個45度”其實是在描述子的階段不是方向分配階段兩者別搞混了。2.4 描述子生成128維向量的形成過程最后一步是關(guān)鍵。SIFT的描述子本質(zhì)上是對特征點鄰域內(nèi)梯度信息的一種編碼。算法取特征點周圍一個16×16的鄰域把它劃分成4×4的網(wǎng)格每個網(wǎng)格是4×4的小塊。在每個小塊上統(tǒng)計8個方向的梯度直方圖每個方向45度這樣4×4個網(wǎng)格×8個方向就得到了4×4×8128維的特征向量。生成描述子之前會先把整個區(qū)域旋轉(zhuǎn)到主方向這正是旋轉(zhuǎn)不變性的最終保障。梯度幅值還會根據(jù)離特征點的距離加權(quán)靠近中心點的像素貢獻(xiàn)更大遠(yuǎn)離的貢獻(xiàn)小這樣做能削弱邊緣效應(yīng)和局部遮擋的影響。最后的歸一化處理值得一提。描述子向量生成后會先做一次L2歸一化消除光照變化帶來的整體亮度差異。然后還要把大于0.2的梯度值截斷重新做一次歸一化。這一步是為了減少大梯度值對匹配的過度影響避免幾個極端的強(qiáng)邊緣像素主導(dǎo)了整條描述子的方向。這兩次歸一化做完描述子對光照變化的魯棒性就有了質(zhì)的提升。3. 實操過程SIFT特征提取與匹配完整實現(xiàn)理論說完了直接上實操。我用OpenCV的Python接口演示完整的SIFT特征點檢測、描述子計算和特征匹配流程。這個流程是我在幾個實際項目里反復(fù)調(diào)整后固定下來的版本穩(wěn)定性很高可以直接復(fù)制到你的代碼里改改用。3.1 環(huán)境準(zhǔn)備與圖像預(yù)處理首先確保環(huán)境里有OpenCV。新版OpenCV中SIFT已經(jīng)移到了主庫不需要額外安裝擴(kuò)展模塊。我用的版本是4.8.x新老版本接口基本一致。import cv2 import numpy as np # 讀取圖像 img1 cv2.imread(scene_left.jpg) img2 cv2.imread(scene_right.jpg) # 轉(zhuǎn)灰度SIFT只處理單通道圖像 gray1 cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) gray2 cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY) # 可選預(yù)處理。實際項目里建議做一次去噪推薦高斯模糊 # 核大小根據(jù)圖清晰度調(diào)整默認(rèn)(3,3) gray1 cv2.GaussianBlur(gray1, (3, 3), 0) gray2 cv2.GaussianBlur(gray2, (3, 3), 0)這里有個容易被忽略的點SIFT內(nèi)部本身有自己的尺度空間理論上對噪聲有一定抵抗力但輸入圖像如果噪聲太強(qiáng)產(chǎn)生的虛假極值點會顯著增加。所以做一次輕度的去噪是劃算的。核大小別太大否則會把細(xì)紋理磨掉反而影響特征點質(zhì)量。高清大圖建議先做一次降采樣再送入SIFT比如把4000×3000的圖片縮到2000×1500。SIFT的計算量跟像素數(shù)近乎線性大圖直接跑不僅慢特征點數(shù)量還可能爆炸導(dǎo)致后面的匹配階段內(nèi)存占用過高。我做過的最極端的一次一張8000×6000的航拍圖默認(rèn)參數(shù)直接檢測出了十幾萬個特征點匹配階段直接卡死。降采樣是性價比非常高的預(yù)處理手段。3.2 特征點檢測與描述子計算# 創(chuàng)建SIFT檢測器 sift cv2.SIFT_create(nfeatures5000, nOctaveLayers3, contrastThreshold0.04, edgeThreshold10, sigma1.6) # 執(zhí)行檢測與描述子計算 kp1, des1 sift.detectAndCompute(gray1, None) kp2, des2 sift.detectAndCompute(gray2, None) print(f左圖檢測到 {len(kp1)} 個特征點) print(f右圖檢測到 {len(kp2)} 個特征點) print(f描述子維度: {des1.shape})關(guān)于nfeatures參數(shù)多說兩句。默認(rèn)是0表示不限制特征點數(shù)量這樣一來紋理豐富的圖很容易產(chǎn)生幾萬個特征點。但數(shù)量多不代表匹配質(zhì)量高反而拖慢速度。我通常根據(jù)實際場景限制在3000到10000之間。如果你做的是兩兩拼接5000足夠用做SLAM或者視覺定位這種需要匹配大量幀的任務(wù)2000到3000是更合理的選擇再多就是浪費(fèi)算力。detectAndCompute是一個合成接口等于先detect再compute。某些老教程會讓你分開調(diào)用但其實這個接口效率更高一個函數(shù)就把關(guān)鍵點和描述子都算好了。3.3 特征匹配與比例測試篩選拿到兩組描述子之后就是核心的匹配環(huán)節(jié)。我推薦用BFMatcher配合KNN匹配然后用Lowe在SIFT原論文里提出的ratio test做篩選。# 創(chuàng)建BFMatcher默認(rèn)使用L2距離 bf cv2.BFMatcher(cv2.NORM_L2) # KNN匹配取最近的兩個鄰居 matches bf.knnMatch(des1, des2, k2) # 比例測試篩選 good_matches [] for m, n in matches: if m.distance 0.75 * n.distance: good_matches.append(m) print(f初始匹配對: {len(matches)}篩選后: {len(good_matches)})ratio test的原理值得好好理解一下。對左圖里的每一個特征點算法在右圖里找出距離最近的兩個點最佳匹配記為m次佳匹配記為n。如果最佳匹配真的可靠它和次佳匹配之間的距離差距會很大如果兩者距離差不多說明這個特征點在右圖里有多個相近的候選本身區(qū)分度就不夠很容易誤匹配所以寧可丟掉。ratio參數(shù)的選擇直接關(guān)系到匹配精度與召回率的平衡。Lowe論文里給的經(jīng)驗值是0.8但我實測下來0.75是個更好的平衡點。如果做的是精度要求高的場景比如三維重建的基礎(chǔ)矩陣估計可以放到0.7甚至0.65代價是會丟一些正確匹配如果做的是圖像檢索這類追求召回率的任務(wù)0.8也可以接受。3.4 單應(yīng)性矩陣估計與結(jié)果可視化匹配對再多里面也難免混著一些錯誤匹配。這時候就需要RANSAC出馬了。RANSAC的思想很簡單隨機(jī)抽取少量匹配對估計出一個幾何變換模型然后統(tǒng)計有多少匹配對符合這個模型反復(fù)迭代最后保留符合數(shù)量最多的模型。# 提取匹配點坐標(biāo) src_pts np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) dst_pts np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) # RANSAC求解單應(yīng)性矩陣 H, mask cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) inlier_matches [m for i, m in enumerate(good_matches) if mask[i]] print(fRANSAC內(nèi)點數(shù)量: {len(inlier_matches)}外點數(shù)量: {len(good_matches) - len(inlier_matches)})這里的5.0是重投影誤差閾值單位是像素。小于5個像素的匹配算內(nèi)點大于的算外點。這個值也要根據(jù)實際場景調(diào)整圖像質(zhì)量高、匹配精度要求高的場景可以設(shè)小一點比如3.0圖像本身模糊或壓縮嚴(yán)重的場景可以放寬到8.0甚至10.0。注意閾值設(shè)大了會把一些錯誤匹配也當(dāng)成內(nèi)點導(dǎo)致求出的H矩陣精度下降。可視化結(jié)果非常簡單# 繪制匹配圖 result cv2.drawMatches(img1, kp1, img2, kp2, inlier_matches, None, flagscv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) cv2.imwrite(sift_matches_result.jpg, result)最后檢查一下如果內(nèi)點數(shù)量占篩選后匹配總數(shù)量的比例太低比如低于50%說明初始匹配質(zhì)量就很差這時候先別急著調(diào)RANSAC參數(shù)而是要回到前面去看contrastThreshold、ratio這些參數(shù)是否設(shè)置合理。4. 性能實測對比SIFT與其他特征描述子的效果差異光說理論有點虛我拿一組真實圖片做了一次完整的對比測試。測試條件兩張有輕微旋轉(zhuǎn)、縮放和曝光差異的風(fēng)景照分辨率1280×720。算法檢測點數(shù)量正確匹配數(shù)匹配精度耗時(ms)SIFT2487623約91%約185SURF2310521約85%約120ORB3000217約60%約28AKAZE1804389約78%約35BRISK1452302約82%約40說明一下SURF的測試用的OpenCV里已不內(nèi)置我是用第三方實現(xiàn)跑的結(jié)果僅供參考。ORB在特征點數(shù)上并不吃虧吃虧的是匹配精度——在旋轉(zhuǎn)和尺度變化明顯的條件下ORB的二進(jìn)制描述子區(qū)分度明顯不足匹配質(zhì)量掉得很厲害。如果你只是做快速的對象檢測ORB夠用但要做到像素級的幾何變換估計SIFT在精度上的優(yōu)勢就直接體現(xiàn)出來了。這里再放一個我自己實際項目里記錄的真實案例。某次做室內(nèi)場景的三維重建采集的圖像包含大面積的白墻、光滑地板和少量紋理物體。用ORB匹配大量特征點落在白墻上RANSAC求出來的基礎(chǔ)矩陣經(jīng)常優(yōu)化失敗換SIFT后雖然白墻上依然會誤檢一些點但經(jīng)過第一步的對比度過濾和第二步的ratio篩選誤匹配被控制在了可接受范圍重建質(zhì)量明顯提升。SIFT的實測表現(xiàn)也沒讓我失望過。它最讓我放心的一點是在“特征點少而精”這件事上做得非常好。ORB經(jīng)常給你回幾千個點但每個點的信息量都很淺SIFT即使只給你幾百個點每個點的“辨識度”都足夠強(qiáng)后續(xù)幾何驗證反而更容易收斂。這就像做菜一堆食材可能不如幾樣精選食材出彩。當(dāng)然也要誠實地說一句SIFT的速度劣勢確實存在。在CPU上處理高分辨率視頻流SIFT很難跑到實時。如果你需要實時性且對精度要求不那么苛刻AKAZE和BRISK是不錯的折中方案如果你做的是離線任務(wù)或者特征點數(shù)量可以接受降采樣SIFT就是最穩(wěn)的選擇。5. 常見問題與排查技巧實錄這部分是高價值段落全是從實際調(diào)試?yán)飻€出來的經(jīng)驗。我按問題頻率從高到低整理成了速查表每一類都給排查思路和解決方案。5.1 特征點數(shù)量過少匹配失敗這是zui常見的問題。你滿懷期待跑了一通代碼結(jié)果打印出來左圖檢測到100個點右圖檢測到90個點匹配對稀稀拉拉RANSAC直接就崩了。先看contrastThreshold。默認(rèn)0.04對低對比度圖像來說太嚴(yán)厲了大量溫和變化的紋理點被當(dāng)成“不夠突出”直接濾掉了。如果圖像本身是暗光環(huán)境或者大面積平滑區(qū)域把contrastThreshold調(diào)到0.02甚至0.01特征點數(shù)量會有質(zhì)的提升。但注意閾值越低噪聲點也越多后續(xù)全靠ratio和RANSAC來兜底。再看輸入圖像本身。紋理稀疏的圖像比如白墻、天空、桌面先天就沒有足夠的特征候選再調(diào)參數(shù)也救不回來。我處理這類場景的經(jīng)驗是從流程上想辦法而不是死磕SIFT參數(shù)。比如檢查一下你的測試圖像是不是真的存在可匹配的紋理區(qū)域或者嘗試對圖像做CLAHE對比度增強(qiáng)往往能帶來驚喜。5.2 誤匹配太多RANSAC內(nèi)點率低表現(xiàn)篩選后good_matches很多但RANSAC算完內(nèi)點率不到40%H矩陣完全不準(zhǔn)。第一步檢查ratio參數(shù)。0.75篩完還亂就繼續(xù)降到0.7。降ratio的直接效果就是更嚴(yán)格寧可錯殺不可放過。第二步檢查特征點分布。如果所有特征點都集中在圖像某個角落哪怕都是正確匹配計算全局單應(yīng)性時的約束也不足RANSAC容易走入局部最優(yōu)。這時候建議把圖像均勻分塊做特征提取或者干脆手動設(shè)置ROI。還有一個繞不開的坑重復(fù)紋理。瓷磚地板、百葉窗、鐵絲網(wǎng)這類場景局部特征高度相似描述子區(qū)分度天然就差。這種場景下SIFT也會頭疼解決辦法是增加nOctaveLayers讓特征點落在更大的尺度上或者結(jié)合幾何約束比如極線約束做二次過濾。5.3 SIFT運(yùn)行太慢內(nèi)存占用異常如果你處理的是超大分辨率圖片或者需要處理視頻流速度問題會非常突出。我的建議優(yōu)先級是這樣第一降采樣輸入圖像。分辨率減半速度提升接近4倍。很多特征點在高分辨率下才會出現(xiàn)的細(xì)節(jié)在低分辨率下可能表現(xiàn)不出來但對于大多數(shù)實際應(yīng)用拼接、匹配、定位降采樣到2000像素寬度完全夠用。第二限制nfeatures。默認(rèn)0意味著不限制給個合理上限算法會只保留響應(yīng)最強(qiáng)的那一批點速度直接受益。第三控制nOctaveLayers。默認(rèn)3就夠了不需要為了“更穩(wěn)”盲目調(diào)高每多一層計算量就增加一大截。如果處理的是超大批量圖像比如幾千張圖的匹配還有一個思路先用一個快速檢測器比如FAST粗篩出一批候選區(qū)域只在候選區(qū)域內(nèi)跑SIFT。這個方案我實測過時間能省一倍以上匹配精度損失很小。5.4 匹配結(jié)果正確但拼接圖有明顯錯位這種情況通常不是SIFT的問題而是單應(yīng)性估計的精度不夠。RANSAC跑完得到H矩陣之后可以用一個簡單的手段驗證精度把一幅圖的四個角點投影到另一幅圖看投影誤差是否在可接受范圍內(nèi)。如果誤差大于像素級要么提高RANSAC閾值要么降低閾值——聽起來矛盾實際操作中你要看具體是哪種誤差模式。如果H矩陣精度本身沒問題但拼接處出現(xiàn)重影或錯位那大概率是輸入圖像本身存在視差。離相機(jī)太近的物體在兩張圖中位置變化不符合單應(yīng)性模型這時候SIFT無能為力只能考慮基于光流或立體匹配的融合方案。5.5 同參數(shù)下兩次運(yùn)行結(jié)果不一致這是個容易讓人迷惑的問題。SIFT本身是確定性的算法同輸入同參數(shù)結(jié)果必然一致。真正的元兇多半是圖像讀取時發(fā)生了什么變化——比如讀的是視頻流里的不同幀或者某處代碼對圖像做了什么隱式修改。檢查代碼里是否對輸入圖像做了轉(zhuǎn)碼、縮放、通道重排這些操作都可能改變灰度值進(jìn)而導(dǎo)致檢測結(jié)果不同。還有種情況是浮點數(shù)精度問題在極端情況下不同編譯版本的OpenCV對高斯模糊的卷積實現(xiàn)略有差異導(dǎo)致同一臺機(jī)器上不同環(huán)境的結(jié)果不完全一致。這種差異很細(xì)微不構(gòu)成實際問題不用太糾結(jié)。6. 工具選型與關(guān)鍵參數(shù)的調(diào)試心得SIFT雖然好用但工具鏈選不對效率也會大打折扣。我用過的方案里主流的是OpenCV的C/Python接口。C性能更好、部署更靈活適合正式工程Python開發(fā)效率高適合原型驗證和數(shù)據(jù)分析。如果你做的是學(xué)術(shù)研究還可以考慮VLFeat的實現(xiàn)它提供了一些OpenCV沒有的細(xì)節(jié)控制項比如是否計算描述子、是否做方向分配等但上手成本比OpenCV高一些。參數(shù)調(diào)試心得單獨拿出來聊。nfeatures、nOctaveLayers、contrastThreshold、edgeThreshold、sigma這五個參數(shù)我實際工作中最常用的組合是常規(guī)場景nfeatures5000, nOctaveLayers3, contrastThreshold0.04, edgeThreshold10, sigma1.6低紋理場景nfeatures8000, nOctaveLayers4, contrastThreshold0.02, edgeThreshold10, sigma1.6高噪聲場景nfeatures3000, nOctaveLayers3, contrastThreshold0.06, edgeThreshold15, sigma2.0快速原型場景nfeatures2000, nOctaveLayers2, contrastThreshold0.05, edgeThreshold10, sigma1.2sigma初始值1.6是Lowe論文里驗證過的經(jīng)驗值模擬了圖像采集時相機(jī)的模糊效果。除非你有明確的理由否則不要動它。nOctaveLayers影響的是每個八度內(nèi)的層數(shù)層數(shù)越多尺度采樣越密但耗時也線性增加非必要不加。匹配環(huán)節(jié)的參數(shù)也歸到工具選型里一并說。除了前面提到的ratio閾值還有RANSAC的重投影誤差閾值兩者一個管粗篩、一個管細(xì)篩是精度控制的兩道閘門。我做三維重建時ratio取0.7、RANSAC取3.0做圖像檢索時ratio取0.8、RANSAC取8.0。參數(shù)設(shè)置的邏輯永遠(yuǎn)是跟著任務(wù)走的搞清楚任務(wù)的精度需求比死記經(jīng)驗值更重要。最后再分享一個實際工作中的小技巧在我做的某個物體識別Demo中SIFT一直表現(xiàn)優(yōu)秀但速度總差一口氣。后來想到一個辦法先用FAST做一次快速檢測取響應(yīng)最強(qiáng)的前2000個點然后只對這2000個點計算SIFT描述子。SIFT的描述子計算本身不慢慢的地方在于DoG金字塔的關(guān)鍵點搜索。用FAST粗篩等于提前告訴SIFT“別在那些不像是特征點的地方浪費(fèi)時間了”整體耗時能降到原來的60%左右匹配精度和全量SIFT幾乎持平。這個方法在純SIFT速度不夠但又不滿足于ORB精度的場景里是我目前用過的最性價比方案。算起來從最初被SIFT算法繞得云里霧里到后來在數(shù)個項目里靠它穩(wěn)定輸出結(jié)果中間踩過的坑不算少。但SIFT這套設(shè)計思路尤其是尺度空間和描述子歸一化的思想直到今天仍然值得每一個做視覺的人認(rèn)真研究一遍。它不只是“一個特征點算法”更是一套理解圖像局部結(jié)構(gòu)的思維方式。如果你正在被匹配精度折磨不妨先靜下心來把SIFT的原理徹底吃透再回頭看你的問題很可能迎刃而解。