算把SIFT特征提取加速到毫秒級)
簡介本資源是面向計(jì)算機(jī)視覺開發(fā)者與算法工程師的SIFT特征提取GPU加速開源實(shí)現(xiàn)專為解決傳統(tǒng)CPU版SIFT在高分辨率圖像上計(jì)算慢、難以滿足實(shí)時(shí)性需求的痛點(diǎn)而設(shè)計(jì)。依托CUDA并行架構(gòu)完整實(shí)現(xiàn)了尺度空間構(gòu)建、關(guān)鍵點(diǎn)定位、方向賦值與描述符生成等核心流程顯著提升特征提取效率適用于圖像匹配、三維重建、SLAM等對性能敏感的工業(yè)與科研場景。壓縮包共191個(gè)文件含28個(gè)頭文件h與25個(gè)C源碼cpp構(gòu)成主體邏輯9個(gè)Visual Studio工程文件vcxproj支持Windows快速編譯另有8個(gè)說明文本txt、15張示例圖像jpg及多個(gè)編譯中間產(chǎn)物與動態(tài)庫dll/lib整體大小7.55MB結(jié)構(gòu)清晰、開箱即用。目前已有237人學(xué)習(xí)下載提供完整可運(yùn)行的GPU加速SIFT方案包含多平臺批處理腳本如demo1.bat等、OpenGL/CL/CUDA三后端支持代碼ProgramGLSL.cpp/ProgramCL.cpp/ProgramCG.cpp及詳細(xì)授權(quán)說明COPYING便于二次開發(fā)與性能對比實(shí)驗(yàn)。 做三維重建和SLAM的同仁應(yīng)該都有過這種體驗(yàn)CPU版本的SIFT在640×480圖像上提取特征點(diǎn)動不動就是一兩百毫秒起步分辨率上到1080p以后單幀逼近一秒后端圖優(yōu)化算得再快也被特征提取卡住脖子。我第一次在項(xiàng)目里看到SiftGPU是當(dāng)時(shí)跑一個(gè)離線SfM管線特征提取成了整個(gè)流程里最扎眼的瓶頸于是順著“SIFT GPU加速”這個(gè)方向摸到了這個(gè)項(xiàng)目——一個(gè)把SIFT特征提取完整搬到顯卡上的開源庫在NVIDIA顯卡上能把特征點(diǎn)提取壓到毫秒級。這篇文章就圍繞SiftGPU展開講講它的加速路子、編譯部署、接入方式、實(shí)測性能以及我自己踩過的那些坑。適合在做圖像配準(zhǔn)、三維重建、遙感拼接、SLAM的同學(xué)參考順便也給想理解“GPU并行到底怎么改造串行算法”的人留一份不算枯燥的拆解。1. 為什么SIFT會成為性能瓶頸1.1 SIFT算法流程里最耗時(shí)的幾步SIFT全稱Scale-Invariant Feature Transform尺度不變特征變換在深度學(xué)習(xí)特征鋪開之前它幾乎是幾何視覺的標(biāo)配。它整個(gè)流程拆開大概分五步構(gòu)建高斯金字塔對原圖做一系列不同尺度的高斯模糊生成多層圖像。生成DoGDifference of Gaussian差分金字塔相鄰尺度相減。極值點(diǎn)檢測在DoG空間的26鄰域里找局部極值點(diǎn)作為候選特征點(diǎn)。關(guān)鍵點(diǎn)精確定位與過濾用Taylor展開做亞像素定位用Hessian矩陣去除邊緣響應(yīng)點(diǎn)。方向分配與描述子生成在特征點(diǎn)鄰域統(tǒng)計(jì)梯度方向直方圖生成128維向量。表面上看每一步都不算復(fù)雜真正的問題在于“重復(fù)次數(shù)”。一個(gè)普通的高斯金字塔會有3到4個(gè)octave每個(gè)octave里又有3到5層合起來就是十幾張不同尺度的圖。每一層都要做整圖高斯卷積卷積核還要隨尺度變化DoG全圖減法極值點(diǎn)檢測在26鄰域里逐像素比較——這些操作全部落在圖像像素級別而且彼此獨(dú)立。CPU版本的實(shí)現(xiàn)不管怎么優(yōu)化循環(huán)、怎么用SIMD指令本質(zhì)上還是在用幾十上百個(gè)核心去跑幾百萬個(gè)像素的任務(wù)嚴(yán)重核心饑餓。1.2 為什么GPU是天然加速器GPU和CPU的差異一句話就能說清CPU核心少但單核強(qiáng)GPU核心多但單核弱。SIFT里那些“全圖逐像素做同樣運(yùn)算”的操作完美命中GPU的Single Program Multiple Data模式——同一個(gè)shader程序幾千個(gè)線程同時(shí)跑在不同像素上數(shù)據(jù)天然并行。我一開始也覺得SIFT里有不少分支判斷和動態(tài)列表操作GPU不一定擅長。但真正把流程捋一遍就會發(fā)現(xiàn)耗時(shí)的部分90%以上是圖像處理的并行操作高斯卷積的每個(gè)輸出像素只依賴鄰域輸入DoG每個(gè)像素只是兩個(gè)紋理采樣的差值極值點(diǎn)檢測每個(gè)像素只要和26個(gè)鄰居比大小。這些操作不需要CPU參與任何復(fù)雜調(diào)度往GPU里一丟就能跑滿。GPU加速的核心不只是“快”而是“不搬數(shù)據(jù)”——中間結(jié)果全部留在顯存紋理里CPU和GPU之間只交換最終的特征點(diǎn)列表和描述子這正好避開了PCIe帶寬這個(gè)最大的性能殺手。2. SiftGPU的核心加速思路2.1 數(shù)據(jù)駐留金字塔與紋理SiftGPU的源碼其實(shí)量不大但讀起來有點(diǎn)吃力因?yàn)樗且設(shè)penGL渲染管線的思路來組織GPU計(jì)算的——如果你習(xí)慣CUDA那種“寫kernel、開線程塊”的模型一開始會不適應(yīng)。它的核心思路是把每一步計(jì)算都轉(zhuǎn)成OpenGL的離屏渲染操作。高斯金字塔每一層圖像都存成一張紋理高斯模糊用Separable Gaussian分離高斯實(shí)現(xiàn)先水平方向一維卷積再垂直方向一維卷積每次卷積通過一個(gè)Fragment Shader完成輸出到Frame Buffer ObjectFBO上。DoG層也是類似兩個(gè)相鄰尺度紋理在shader里做減法直接渲染成新的差分紋理。中間過程完全沒有CPU參與所有中間結(jié)果都待在顯存里。極值點(diǎn)檢測稍微麻煩一點(diǎn)因?yàn)橐容^26鄰域。SiftGPU的做法是檢測階段也以紋理為單位并行處理shader里對每個(gè)像素采樣周圍26個(gè)位置判斷它是不是局部極值。通過判斷的坐標(biāo)會作為候選點(diǎn)寫進(jìn)一個(gè)輸出結(jié)構(gòu)后續(xù)再交給CPU或GPU進(jìn)一步做精確定位。這里能感受到老GPU編程的獨(dú)特味道——不一定用什么高級API但每一步都能對應(yīng)到“渲染到目標(biāo)”這個(gè)樸素的并行框架上。2.2 GLSL實(shí)現(xiàn)和CUDA實(shí)現(xiàn)的區(qū)別SiftGPU最初的主線是基于OpenGL的GLSL實(shí)現(xiàn)后來才加入CUDA版本。為什么選GLSL而不是CUDA作為第一版時(shí)間點(diǎn)很關(guān)鍵SiftGPU是2008年前后的項(xiàng)目CUDA還在早期階段生態(tài)遠(yuǎn)不如現(xiàn)在成熟而OpenGL在Windows、Linux、Mac上都能跑GLSL 1.20版本的shader幾乎什么顯卡都支持跨平臺成本低很多。加上SIFT這個(gè)算法本身大量操作就是圖像卷積和鄰域統(tǒng)計(jì)用渲染管線做非常自然。后來加入的CUDA版本主要優(yōu)化在描述子生成階段。描述子生成需要每個(gè)特征點(diǎn)在鄰域內(nèi)統(tǒng)計(jì)梯度直方圖雖然并行度也不低但GLSL里做局部數(shù)據(jù)累積比較別扭。CUDA的線程模型更適合這種“一個(gè)線程處理一個(gè)特征點(diǎn)”的任務(wù)可以把描述子的統(tǒng)計(jì)和歸一化做得更高效。源碼里可以通過宏SIFTGPU_ENABLE_CUDA控制是否編譯CUDA分支如果只做OpenGL版本可以不開啟這個(gè)宏省去一堆CUDA依賴。3. 編譯部署源碼要點(diǎn)與依賴坑3.1 源碼結(jié)構(gòu)和依賴梳理SiftGPU的源碼包不大核心代碼在src/SiftGPU目錄下包含SiftGPU.h、SiftGPU.cpp和一堆Shader相關(guān)的文件。CLI入口在src/CLI里編譯后會生成一個(gè)siftgpu命令行工具可以用來快速驗(yàn)證效果和產(chǎn)特征點(diǎn)文件。整個(gè)庫對外的主要接口就是SiftGPU類使用起來并不復(fù)雜。Linux下編譯需要三個(gè)基礎(chǔ)依賴GLEWOpenGL擴(kuò)展加載庫、DevIL圖像讀寫庫CLI工具用來加載圖片、X11相關(guān)頭文件。這里說一個(gè)容易踩的坑網(wǎng)上很多倉庫的SiftGPU代碼是早期版本在較新的gcc版本上編譯會報(bào)一些莫名其妙的錯誤比如GL_EXT_texture_rectangle相關(guān)的枚舉找不到。這種問題多半是GLEW版本過新導(dǎo)致的建議先把GLEW升級到2.x再編譯。如果遇到非要在老環(huán)境里編譯可以手動在編譯選項(xiàng)里加上-DGL_GLEXT_PROTOTYPES部分問題能繞過去。3.2 Linux編譯步驟在Ubuntu/Debian系環(huán)境里依賴裝齊后編譯本身很痛快的sudo apt-get install libglew-dev libdevil-dev libx11-dev make -f makefile_x11編譯完成后bin目錄下會生成siftgpu可執(zhí)行文件lib目錄下生成靜態(tài)庫libSiftGPU.a。庫文件可以直接鏈接到自己的項(xiàng)目里頭文件指向src/SiftGPU/SiftGPU.h。離線環(huán)境編譯是個(gè)常見的真實(shí)場景。我遇到過內(nèi)網(wǎng)機(jī)器上裝不了apt包的解決辦法是去另一臺同版本系統(tǒng)的機(jī)器上把libglew-dev、libdevil-dev的deb包下載過來用dpkg -i手動安裝。DevIL這個(gè)庫比較老二十年前的接口風(fēng)格但功能穩(wěn)定裝完就再也不需要操作它了編譯好后直接鏈接即可。3.3 Windows編譯簡要說明Windows下編譯麻煩一些但也不算難。源碼里帶Visual Studio的工程文件需要提前裝好GLEW并配置好包含目錄和庫目錄。DevIL在Windows下是可選的如果不需要CLI工具編譯核心庫只用GLEW就夠了。Windows下編譯有個(gè)常見問題Debug配置下運(yùn)行會崩潰Release配置下沒問題。這多半是GLEW庫的運(yùn)行時(shí)庫設(shè)置/MD和/MT和主工程不一致導(dǎo)致的把整個(gè)解決方案統(tǒng)一成Release /MD就能解決。另一個(gè)問題是OpenGL上下文——SiftGPU默認(rèn)需要自己創(chuàng)建OpenGL上下文在Windows上如果沒有在窗體里初始化好GLCreateGLContext會失敗。CLI工具里帶了一個(gè)w窗口但你在自己的工程里調(diào)用時(shí)需要先創(chuàng)建好GL上下文或者用SiftGPU自帶的無窗口上下文創(chuàng)建邏輯這塊后面接入章節(jié)細(xì)說。4. 在項(xiàng)目里接入SiftGPU4.1 CLI工具怎么用SiftGPU自帶的命令行工具是驗(yàn)證效果最快的方式雖然它一般不在正式算法鏈路里用但很適合拿來做“SiftGPU到底能不能跑”的冒煙測試?;居梅╞in/siftgpu input.pgm -lowe -display 0 -o output.key說明幾個(gè)常用參數(shù)-lowe使用Lowe論文里的默認(rèn)參數(shù)包括對比度閾值和邊緣閾值這個(gè)參數(shù)組合最均衡實(shí)際工程里建議直接用它。-fo n第一個(gè)octave的索引默認(rèn)是0。如果圖像比較大可以設(shè)為負(fù)值讓金字塔往更大尺度方向擴(kuò)展但特征點(diǎn)數(shù)量會增加計(jì)算量也會上去。-display 0不彈顯示窗口在純命令行環(huán)境下必須加否則會嘗試創(chuàng)建窗口。-o output.key把特征點(diǎn)寫入文件。CLI支持PGM、PNG、JPG等格式DevIL支持的格式基本都能讀但要注意DevIL對PNG灰度圖的讀取兼容性一般如果輸入圖像讀出異常先轉(zhuǎn)成PGM再試。4.2 C API調(diào)用示例在算法工程里一般不會用CLI而是直接調(diào)庫。標(biāo)準(zhǔn)調(diào)用流程是初始化上下文、設(shè)置圖像尺寸、喂入圖像數(shù)據(jù)、拉取特征點(diǎn)。核心代碼長這樣#include SiftGPU.h #include vector int run_sift(unsigned char* gray_data, int width, int height, std::vectorfloat keypoints, std::vectorfloat descriptors) { SiftGPU* siftgpu new SiftGPU(); // 命令行參數(shù)解析這里用默認(rèn)的Lowe參數(shù) char* argv[] {siftgpu, -lowe, -fo, 0, -display, 0}; siftgpu-ParseParam(6, argv); // 創(chuàng)建OpenGL上下文這一步可能失敗 if (siftgpu-CreateGLContext() ! SiftGPU::SIFTGPU_FULL_SUPPORTED) { delete siftgpu; return -1; } siftgpu-SetImageSize(width, height); // 喂入灰度圖數(shù)據(jù) int status siftgpu-RunSIFT(width, height, gray_data, GL_LUMINANCE, GL_UNSIGNED_BYTE); if (status ! 0) { delete siftgpu; return -2; } // 獲取特征點(diǎn)數(shù)量 int num siftgpu-GetFeatureCount(); // 拉取特征點(diǎn)位置/尺度和描述子 std::vectorSiftGPU::SiftKeypoint keys(num); std::vectorfloat desc(128 * num); siftgpu-GetFeatureVector(keys.data(), desc.data()); // 整理成自定義結(jié)構(gòu)或者直接交給匹配模塊 keypoints.resize(num * 4); for (int i 0; i num; i) { keypoints[i * 4 0] keys[i].x; // x坐標(biāo) keypoints[i * 4 1] keys[i].y; // y坐標(biāo) keypoints[i * 4 2] keys[i].s; // 尺度 keypoints[i * 4 3] keys[i].o; // 主方向弧度 } descriptors.swap(desc); delete siftgpu; return num; }這里說幾個(gè)容易被坑的細(xì)節(jié)。SiftGPU::SiftKeypoint結(jié)構(gòu)體里x、y是圖像坐標(biāo)s是尺度o是方向弧度和VLFeat里SiftKeypoint的基本一致做特征匹配時(shí)可以直接對齊位置和尺度。描述子是128維浮點(diǎn)數(shù)組順序是“第i個(gè)特征點(diǎn)的第j維”在desc[i * 128 j]里連續(xù)存儲可以直接喂給FLANN或者自寫的最近鄰匹配。4.3 數(shù)據(jù)結(jié)構(gòu)和參數(shù)細(xì)節(jié)SiftGPU的RunSIFT接口有多個(gè)重載上面用的是最常見的灰度圖像數(shù)據(jù)版本。如果喂的是OpenCV的cv::Mat要注意兩點(diǎn)第一灰度圖的排列。cv::Mat在內(nèi)存里可能是帶padding的每行字節(jié)數(shù)不一定是width尤其是width不是4的倍數(shù)時(shí)但SiftGPU內(nèi)部會通過glPixelStorei設(shè)置對齊方式所以從cv::Mat里直接取data指針傳入一般來說都能用。但為了穩(wěn)妥我在實(shí)際項(xiàng)目里統(tǒng)一做了一次cv::cvtColor加clone確保內(nèi)存緊湊連續(xù)省得在灰度圖的寬度對齊問題上排查半夜。第二通道數(shù)。SiftGPU的RunSIFT需要GL_LUMINANCE格式的單通道灰度圖RGB要先轉(zhuǎn)灰度。如果你直接塞GL_RGB它內(nèi)部也會轉(zhuǎn)但性能會差一些而且需要額外注意內(nèi)存布局。我的建議是統(tǒng)一轉(zhuǎn)成單通道再喂。單例上下文的問題也得提前想清楚。SiftGPU類內(nèi)部持有OpenGL上下文如果你在多個(gè)線程里各new一個(gè)實(shí)例要注意OpenGL上下文不能在多個(gè)線程并發(fā)使用要么串行化要么每個(gè)線程創(chuàng)建獨(dú)立上下文。我在一個(gè)多線程拼接程序里直接并發(fā)調(diào)SiftGPU結(jié)果GPU驅(qū)動報(bào)錯后來改成線程池里串行處理SiftGPU部分其他后處理并行才穩(wěn)住。5. 實(shí)測性能到底能快多少倍5.1 實(shí)測數(shù)據(jù)表格性能是大家最關(guān)心的部分。以我自己的實(shí)測經(jīng)驗(yàn)CPU端用OpenCV的SIFT實(shí)現(xiàn)3.4系列也就是官方非contrib版本GPU端用SiftGPU在GTX 1060和RTX 3060上分別測過幾組數(shù)據(jù)大概量級如下圖像分辨率CPU SIFT(OpenCV)SiftGPU GTX 1060SiftGPU RTX 3060加速倍數(shù)1060640×480約120~200ms約3~6ms約2~4ms25~40倍1280×720約400~700ms約10~20ms約6~12ms30~50倍1920×1080約0.8~1.5s約25~50ms約15~30ms30~50倍說明一下這些數(shù)字受具體圖片內(nèi)容影響很大紋理密集、特征點(diǎn)多的場景耗時(shí)更高特征點(diǎn)數(shù)量直接決定描述子生成階段的耗時(shí)。上面數(shù)值是普通室內(nèi)/室外圖像的大致水平。不同顯卡的差距主要體現(xiàn)在shader單元數(shù)量和顯存帶寬上。SiftGPU用到的高斯卷積和DoG都極度依賴紋理采樣性能顯存帶寬越高的卡提速越明顯。GTX 1060和RTX 3060差距不算特別大但和Intel集顯比就是另一個(gè)世界了——Intel集顯跑SiftGPU基本跌回CPU水平有些老集顯甚至不兼容GLSL 1.20的某些特性直接初始化失敗。5.2 影響性能的幾個(gè)因素我自己實(shí)際調(diào)參時(shí)發(fā)現(xiàn)開銷大頭不完全在特征點(diǎn)數(shù)量而在金字塔本身的紋理分配。假設(shè)圖像是1920×1080金字塔有4個(gè)octave、每層5張尺度圖每張圖都會分配紋理內(nèi)存某些octave的圖像尺寸還需要做降采樣。如果顯卡顯存不夠驅(qū)動會把紋理放在系統(tǒng)內(nèi)存里性能直接雪崩。所以大分辨率圖像下先看顯存占用不要只看GPU利用率。還有一個(gè)容易忽略的點(diǎn)SiftGPU的GetFeatureVector回讀操作是同步的會阻塞直到GPU執(zhí)行完。如果你的算法鏈路里每幀都要提取特征點(diǎn)然后馬上匹配這個(gè)同步等待是必要的但如果后續(xù)還有別的GPU操作可以考慮把SiftGPU的多個(gè)步驟分開避免頻繁GPU-CPU同步導(dǎo)致流水線打空。不過SiftGPU的接口粒度比較粗這種工程級優(yōu)化需要自己改造源碼普通項(xiàng)目其實(shí)不用折騰。6. 常見問題與排查記錄SiftGPU畢竟是十多年前的項(xiàng)目使用中遇到問題比現(xiàn)代庫頻繁很多。下面列幾個(gè)我印象深刻的坑和排查思路。6.1 編譯期問題編譯階段最經(jīng)典的就是GLEW和GLSL版本不匹配。典型報(bào)錯是error: ‘GL_TEXTURE_RECTANGLE_EXT’ undeclared這通常是因?yàn)镚LEW頭文件版本太舊不包含這個(gè)擴(kuò)展定義。升級GLEW到2.0以上基本能解決。另一個(gè)是DevIL在較新gcc版本下的頭文件兼容問題如果編譯CLI時(shí)報(bào)uint8_t、uint16_t未定義可以直接在DevIL頭文件前加上#include cstdint或者編譯命令里加-include cstdint。6.2 運(yùn)行期問題運(yùn)行期最典型的是CreateGLContext返回SIFTGPU_ERROR在純服務(wù)器環(huán)境、無顯示器場景下尤其常見。需要檢查兩點(diǎn)顯卡驅(qū)動是否裝好用glxinfo | grep OpenGL version確認(rèn)OpenGL版本在2.1以上。是否有DISPLAY環(huán)境變量。如果沒有GUI-display 0能跳過窗口創(chuàng)建但OpenGL上下文的創(chuàng)建也需要一個(gè)可見或離屏的surface。服務(wù)器上可以裝xvfb提供虛擬顯示或者改走EGL離屏方案SiftGPU官方代碼里沒有直接支持需要自己改不如xvfb-run來得快。我用xvfb-run的方式跑過離線批量提取穩(wěn)定運(yùn)行沒有問題。注意xvfb-run bin/siftgpu input.pgm -display 0這種組合別把-display的參數(shù)丟了。6.3 算法結(jié)果差異問題SiftGPU輸出的特征點(diǎn)位置和描述子與CPU版SIFT基本一致但不是100%完全相同。原因主要是SiftGPU全程使用單精度浮點(diǎn)而CPU版比如VLFeat內(nèi)部部分計(jì)算用double另外高斯金字塔的邊界處理方式也可能有細(xì)微差異。在實(shí)際匹配場景里我見過特征點(diǎn)位置差0.1~0.3像素、描述子距離差0.01左右的情況對匹配影響很小基本可以忽略。但如果你的算法對特征點(diǎn)位置敏感比如做亞像素視覺測量建議做一次交叉驗(yàn)證再決定是否用SiftGPU的結(jié)果直接當(dāng)標(biāo)準(zhǔn)答案。特征點(diǎn)數(shù)量為0也是一個(gè)常見現(xiàn)象。先看圖像是否太暗或者太平滑SIFT閾值天然會把低對比度區(qū)域過濾掉再看是否傳了彩色圖但用了錯誤的格式如果喂RGB數(shù)據(jù)卻標(biāo)成GL_LUMINANCE讀出來的像素值完全錯亂特征點(diǎn)自然出不來。我一般會先保存一張灰度圖出來肉眼確認(rèn)一遍數(shù)據(jù)對不對再調(diào)后面的邏輯。多線程環(huán)境下的坑值得單獨(dú)說一次。SiftGPU的多個(gè)實(shí)例如果共享同一個(gè)OpenGL上下文在并發(fā)調(diào)用時(shí)會有不可預(yù)期的行為。一個(gè)穩(wěn)妥的方案是進(jìn)程里只保留一個(gè)SiftGPU實(shí)例外部用互斥鎖把RunSIFT和GetFeatureVector串行化同時(shí)保證SiftGPU內(nèi)部的對象不跨線程使用。如果想并行處理多張圖像可以每個(gè)線程創(chuàng)建獨(dú)立OpenGL上下文但這樣顯存占用會成倍上升小顯存顯卡可能反而變慢。7. 一些個(gè)人體會與后續(xù)擴(kuò)展SiftGPU這個(gè)項(xiàng)目放在今天看代碼風(fēng)格老、文檔少、接口設(shè)計(jì)也不是很現(xiàn)代但它有一種難得的教學(xué)價(jià)值它把SIFT這樣一個(gè)流程復(fù)雜、分支多、有人工設(shè)計(jì)痕跡的算法用受限的GPU編程模型完整實(shí)現(xiàn)了一遍。我第一次讀它源碼時(shí)比看CUDA教程理解得深不少——因?yàn)槟惚仨毟闱宄總€(gè)階段的并行度在哪哪些操作適合留在GPU里哪些必須回讀CPU。這個(gè)思路至今還在用哪怕是后來用深度學(xué)習(xí)特征替換傳統(tǒng)特征分析“哪段計(jì)算能吃滿并行單元”的判斷方式一直沒變。項(xiàng)目本身也給出了一個(gè)很典型的工程判斷如果一個(gè)算法要長期用、性能瓶頸明顯且操作具有圖像并行特征那就值得用GPU專門優(yōu)化一把。SiftGPU做的正是這件事而且做得比較徹底??紤]到現(xiàn)在低功耗異構(gòu)計(jì)算芯片架構(gòu)的趨勢——專用加速單元比如NPU/APU和通用CPU/GPU組合跑異構(gòu)負(fù)載——其實(shí)和SiftGPU當(dāng)年“把專用任務(wù)放到專用并行單元上”的思路同源。你在新平臺上設(shè)計(jì)一套視覺處理管線時(shí)如果遇到SIFT這類老算法優(yōu)先看看有沒有官方或社區(qū)的GPU實(shí)現(xiàn)別自己從零開始寫。實(shí)在要寫參考SiftGPU的分塊思路也能少走很多彎路。最后分享一個(gè)自己常用的小技巧如果我只想在Linux下快速批量提取SiftGPU特征點(diǎn)做數(shù)據(jù)集預(yù)處理我會編譯好CLI工具然后用xvfb-run加一個(gè)循環(huán)腳本把每張圖的特征點(diǎn)和描述子輸出成二進(jìn)制文件。這樣不需要寫C程序就能出數(shù)據(jù)后續(xù)讀數(shù)據(jù)再按要求解析。整個(gè)過程穩(wěn)、快、還不用操心OpenGL上下文。這個(gè)工具我備份了好幾個(gè)平臺的版本每次搭新環(huán)境第一件事就是把SiftGPU編譯出來——雖然平時(shí)用得不多但一旦遇到SIFT相關(guān)的活它總是最可靠的那把舊扳手。本文還有配套的精品資源點(diǎn)擊獲取