展contrib編譯完整指南)
如果你最近在一個(gè)老項(xiàng)目里接手了OpenCV 3.3.0大概率也繞不開這個(gè)標(biāo)題本身Win10 VS2015 OpenCV3.3.0下擴(kuò)展OpenCV_contrib3.3.0。OpenCV 3.3.0官方只提供標(biāo)準(zhǔn)模塊的預(yù)編譯包SIFT、SURF、face、aruco這些擴(kuò)展模塊都放在contrib倉庫里而官方二進(jìn)制是不包含它們的。更麻煩的是這個(gè)版本太老網(wǎng)上教程大多已經(jīng)轉(zhuǎn)向OpenCV 4.x照著新版流程做第一輪CMake配置就對不上。這篇文章就把我自己完整走通過一遍的流程記錄下來從版本選擇、環(huán)境準(zhǔn)備到CMake配置、VS2015編譯、安裝再到用SIFT驗(yàn)證contrib是否真正生效。適合正在搭建VS2015環(huán)境下OpenCV 3.3.0源碼編譯環(huán)境的人也適合買了帶舊SDK的工控機(jī)、只能鎖死在VC14工具集里做視覺開發(fā)的人。1. 為什么一個(gè)2017年的老組件到現(xiàn)在還有人折騰1.1 contrib擴(kuò)展包到底補(bǔ)了什么OpenCV的主倉庫在3.x時(shí)代就已經(jīng)把標(biāo)準(zhǔn)模塊拆得很細(xì)核心、圖像處理、視頻、DNN等都在里面但歷史遺留的xfeatures2d、face、aruco、text、optflow這些實(shí)驗(yàn)性或?qū)@舾心K一直放在獨(dú)立倉庫里。典型的就是SIFT和SURF。OpenCV 3.3.0里你去主倉庫的opencv2/features2d.hpp里找SIFT是找不到的它被挪到了opencv2/xfeatures2d.hpp而這個(gè)頭文件只存在于contrib擴(kuò)展倉庫的模塊里。所以“擴(kuò)展contrib”不是一個(gè)可選項(xiàng)而是必須做的編譯步驟。很多人下載了官方提供的opencv-3.3.0-vc14解壓包然后發(fā)現(xiàn)代碼里寫#include opencv2/xfeatures2d.hpp時(shí)報(bào)文件找不到或者SIFT函數(shù)鏈接時(shí)報(bào)未定義符號本質(zhì)原因就是官方預(yù)編譯包里沒有編譯contrib模塊。更關(guān)鍵的是你不能指望把contrib的源碼文件夾塞進(jìn)已有官方包里碰運(yùn)氣。OpenCV的構(gòu)建系統(tǒng)在主倉庫源碼里通過CMake組織contrib是構(gòu)建時(shí)參與進(jìn)來的擴(kuò)展模塊集合最終會被一起編進(jìn)opencv_world330.dll或者拆分的多個(gè)lib中。想用SIFT就必須從源碼重新編譯主倉庫加contrib沒有捷徑。1.2 版本三方咬合錯(cuò)一步滿盤皆輸這個(gè)標(biāo)題里的三個(gè)版本號其實(shí)咬得很死。Windows 10對VS2015本身沒有兼容問題但VS2015必須打到Update 3否則OpenCV 3.3.0的C11代碼在編譯中會遇到一些編譯器內(nèi)部錯(cuò)誤。OpenCV 3.3.0主倉庫的構(gòu)建腳本對CMake版本有隱形的最低要求CMake太老無法識別VS2015的x64生成器CMake太新又可能因?yàn)椴呗宰兏a(chǎn)生一堆莫名其妙的問題。contrib倉庫必須和主倉庫選擇同一個(gè)tag。主倉庫下載的是3.3.0contrib也要下載3.3.0。如果一不留神拉了master分支OpenCV主倉庫3.3.0的某些內(nèi)部API簽名和contrib最新代碼對不上后期編譯報(bào)錯(cuò)會成片出現(xiàn)且錯(cuò)誤信息根本不像版本沖突。我見過不少配置失敗案例最后查下來都是contrib版本拉錯(cuò)。遇到過拉成3.4.x分支然后去配3.3.0主倉庫的編譯xfeatures2d模塊時(shí)幾十個(gè)頭文件找不到。這屬于白花一小時(shí)換回匹配版本后立刻通過。2. 環(huán)境準(zhǔn)備里最容易翻車的三個(gè)點(diǎn)2.1 VS2015 Update 3不是可選項(xiàng)如果你是第一次在這臺機(jī)器上安裝VS2015安裝器里務(wù)必把“Visual C”工具集勾上同時(shí)勾選“Windows 10 SDK”。許多人只裝了.NET相關(guān)組件等到CMake一跑發(fā)現(xiàn)找不到C編譯器那叫一個(gè)摸不著頭腦。安裝完以后打開Visual Studio的“幫助–關(guān)于”確認(rèn)版本號是14.0.25420.00以上。像這樣的版本號對應(yīng)VS2015 Update 3。Update 3修復(fù)了不少對Windows 10 SDK的識別問題也補(bǔ)了C標(biāo)準(zhǔn)庫實(shí)現(xiàn)的一大堆bug。OpenCV 3.3.0源碼里大量使用C11特性所以在VS2015上至少要這個(gè)Update。還有一個(gè)坑VS2015的內(nèi)部版本號是14.0所以CMake生成器名稱叫Visual Studio 14 2015 Win64不要以為是選錯(cuò)了。2.2 CMake版本不要盲目追新很多人習(xí)慣去CMake官網(wǎng)下載一個(gè)“最新穩(wěn)定版”然后就開始configure。OpenCV 3.3.0是2017年發(fā)布的它的CMake腳本默認(rèn)不認(rèn)太新的策略規(guī)則。我常用的穩(wěn)定組合是CMake 3.8.2這套組合在Win10 VS2015 OpenCV 3.3.0上跑了很多次CMake GUI沒有紅色告警生成OpenCV.sln也干凈。如果你電腦里已經(jīng)裝了新版本CMake也沒必要卸載重來。配置時(shí)看到一串關(guān)于CMP0025、CMP0075的策略警告時(shí)就要有心理準(zhǔn)備新CMake可能在后續(xù)步驟里因?yàn)椴煌呗越oOpenCV生成不同的項(xiàng)目結(jié)構(gòu)。我實(shí)測過用CMake 3.28去配置OpenCV 3.3.0能生成工程但部分contrib模塊的下載路徑處理方式變了導(dǎo)致xfeatures2d的依賴文件下載位置異常最后編譯階段報(bào)文件找不到。老老實(shí)實(shí)換回3.8.2一次通過。2.3 源碼目錄結(jié)構(gòu)與下載方式下載OpenCV源碼時(shí)我建議在GitHub上按tag下載zip不要用master分支。主倉庫選擇3.3.0這個(gè)tag文件解壓后目錄名一般是opencv-3.3.0。contrib倉庫同樣選擇3.3.0解壓后目錄名一般是opencv_contrib-3.3.0。目錄結(jié)構(gòu)我習(xí)慣這樣擺D:\ ├── opencv-3.3.0 ├── opencv_contrib-3.3.0 └── opencv-3.3.0-buildopencv-3.3.0-build是CMake的build目錄也就是最終生成VS工程文件的地方。三個(gè)路徑不要出現(xiàn)中文、空格或者圓括號。別看這條像廢話曾經(jīng)有人放在C:\Users\張三\下載\里CMake的下載緩存路徑處理中文有問題configure階段一直報(bào)路徑找不到。3. CMake配置過程逐項(xiàng)拆解3.1 第一次Configure與生成器選擇打開CMake GUIWhere is the source code填D:/opencv-3.3.0Where to build the binaries填D:/opencv-3.3.0-build。然后點(diǎn)Configure。彈出選擇框后下拉列表里選Visual Studio 14 2015 Win64注意一定要帶Win64這是關(guān)鍵。如果你選成不含Win64的Visual Studio 14 2015生成的是32位工程的solution后面鏈接x64的OpenCV庫全部失敗。平臺選完以后下面的編譯器默認(rèn)值不要?jiǎng)又苯覨inish。第一次Configure結(jié)束以后紅色高亮條目會很多這是正常的。因?yàn)楹芏嘁蕾囘€沒有解析完成你接下來要做的就是在搜索框里逐個(gè)設(shè)置關(guān)鍵變量然后再次Configure。3.2 OPENCV_EXTRA_MODULES_PATH設(shè)置錯(cuò)誤是最普遍原因在CMake GUI搜索OPENCV_EXTRA_MODULES_PATH把這個(gè)變量指向contrib倉庫里的modules目錄。正確寫法D:/opencv_contrib-3.3.0/modules千萬別寫成D:/opencv_contrib-3.3.0。OpenCV構(gòu)建系統(tǒng)會在這個(gè)路徑下直接掃描modules子目錄里各個(gè)模塊的CMakeLists.txt。如果你填的是contrib的根目錄雖然也能配置通過但最后生成的模塊列表里只有主倉庫的標(biāo)準(zhǔn)模塊contrib根本沒有參與構(gòu)建。如果你想判斷contrib是否真正被識別不用急著Generate。在CMake的輸出窗口里找到OpenCV modules那一節(jié)正常情況下應(yīng)該能看到xfeatures2d face aruco text ...看到這些模塊才說明擴(kuò)展路徑生效了。3.3 必勾與可不勾的選項(xiàng)清單第二次Configure以后紅色大部分會消失。這時(shí)候在搜索框里繼續(xù)處理幾個(gè)關(guān)鍵開關(guān)。必勾的變量名推薦值原因BUILD_opencv_worldON把所有OpenCV模塊統(tǒng)一編譯成一個(gè)opencv_world330庫測試程序鏈接時(shí)只需要一個(gè)庫文件OPENCV_ENABLE_NONFREEON啟用SIFT、SURF等受專利保護(hù)的算法不勾的話即使編譯成功運(yùn)行時(shí)也會提示算法被禁用可以不勾的變量名推薦值原因BUILD_EXAMPLESOFF示例代碼不是必需品關(guān)掉能省不少編譯時(shí)間BUILD_TESTSOFF官方測試模塊很大只做二次開發(fā)沒必要編譯BUILD_PERF_TESTSOFF同理BUILD_opencv_python2/BUILD_opencv_python3按需如果不用Python調(diào)用OpenCV可以關(guān)掉少編兩個(gè)大模塊BUILD_opencv_javaOFF用不到就關(guān)掉如果你只做C圖像處理和特征提取關(guān)掉測試、示例和Python編譯時(shí)間能少三分之一以上。四核八線程的機(jī)器實(shí)測半小時(shí)左右能把Release版本編完。這里有個(gè)容易被忽略的點(diǎn)BUILD_opencv_world開啟以后contrib里的xfeatures2d等模塊也會被編進(jìn)world庫不沖突。所以后面鏈接時(shí)只寫opencv_world330.lib就夠用。3.4 網(wǎng)絡(luò)下載項(xiàng)IPP與FFmpeg的處理思路CMake一旦開始Configure會嘗試從遠(yuǎn)程服務(wù)器下載一些第三方依賴。OpenCV 3.3.0主要涉及兩個(gè)東西IPP加速庫和FFmpeg預(yù)編譯包。IPP本身是對圖像計(jì)算的硬件加速優(yōu)化默認(rèn)情況下OpenCV會下載一份編進(jìn)庫中。如果網(wǎng)絡(luò)比較波configure會卡在這里提示超時(shí)或CRC校驗(yàn)錯(cuò)誤。解決辦法很簡單圖像特征提取這類應(yīng)用場景IPP關(guān)掉不影響功能只會讓某些像素級操作慢一點(diǎn)。在搜索框里找WITH_IPP把它取消勾選。同理如果不用視頻讀寫功能可以連WITH_FFMPEG一起關(guān)掉。配置完以后再次Configure這一步能繞開大部分下載問題。如果你確實(shí)需要用OpenCV讀取視頻FFmpeg預(yù)編譯包又是必須的話那就在configure時(shí)保持WITH_FFMPEG打開多試幾次或者找一個(gè)網(wǎng)絡(luò)空閑的時(shí)間段繼續(xù)。不要無限重試下載文件會緩存在C:/Users/用戶名/.cache/opencv3/ffmpeg下某一次成功后文件會留在那里后續(xù)重新configure就不用再下載了。3.5 第三次Configure與Generate所有選項(xiàng)調(diào)整完后再點(diǎn)Configure。觀察輸出窗口如果已經(jīng)沒有紅色高亮而且OpenCV modules列表里出現(xiàn)了contrib模塊就可以點(diǎn)Generate了。Generate速度很快基本一兩秒結(jié)束生成完會在build目錄下產(chǎn)生一個(gè)OpenCV.sln。到了這一步CMake環(huán)節(jié)才算真正通過。很多教程在這里就默認(rèn)成功了其實(shí)不算。我見過不少人在Generate時(shí)彈出CMake Error: The following variables are used in this project, but they are set to NOTFOUND之類的提示大意是某些依賴沒找到。如果你的界面里也出現(xiàn)這個(gè)先不要Generate逐條看哪個(gè)變量是NOTFOUND常見的是PYTHON_EXECUTABLE或JAVA_JVM_LIBRARY。既然用不到Python和Java就把對應(yīng)的BUILD_opencv_python*和BUILD_opencv_java關(guān)閉再Configure。4. VS2015編譯與INSTALL安裝4.1 用Release x64生成ALL_BUILD雙擊OpenCV.slnVisual Studio打開以后先把解決方案配置切到Release把解決方案平臺確認(rèn)在x64。這時(shí)候你會看到一個(gè)龐大的項(xiàng)目列表項(xiàng)目數(shù)量幾十到上百不等取決于你的模塊開關(guān)。在CMakeTargets目錄下找到ALL_BUILD右鍵選擇“生成”。注意第一次編譯不要只單獨(dú)生成INSTALL因?yàn)镮NSTALL依賴所有模塊的lib單獨(dú)生成會報(bào)錯(cuò)“無法打開opencv_core330.lib”之類。編譯過程耗時(shí)很長。在這期間不要隨便點(diǎn)掉正在運(yùn)行的其他編譯任務(wù)。如果某個(gè)模塊失敗VS的“錯(cuò)誤列表”會直接定位到具體項(xiàng)目。這時(shí)候不要慌先看錯(cuò)誤是屬于編譯類還是鏈接類。編譯類錯(cuò)誤通常和源碼版本不匹配有關(guān)鏈接類錯(cuò)誤通常是項(xiàng)目依賴沒生成完整重新生成ALL_BUILD一般能解決。4.2 幾個(gè)低概率但高破壞性的編譯錯(cuò)誤編譯過程中我實(shí)際遇到過兩個(gè)比較典型的坑。第一個(gè)是xfeatures2d模塊編譯時(shí)需要下載boostdesc_bgm.i、vgg_generated_48.i這類訓(xùn)練數(shù)據(jù)文件。這個(gè)下載動作發(fā)生在VS編譯階段如果網(wǎng)絡(luò)不行VS會報(bào)error MSB3073或者提示找不到指定文件。解決辦法是在錯(cuò)誤信息里找到下載URL單獨(dú)用瀏覽器把文件下載下來放到錯(cuò)誤信息提示的緩存目錄再重新生成該模塊。第二個(gè)坑是編譯器報(bào)fatal error C1083: Cannot open include file: opencv2/ximgproc.hpp。這種多半不是文件真的缺失而是你在CMake配置時(shí)把某個(gè)可選模塊關(guān)了但xfeatures2d代碼實(shí)際上依賴它。解決辦法不是去改代碼而是回到CMake GUI里把被關(guān)閉的相關(guān)模塊重新打開重新Configure和Generate然后重新編譯。還有一個(gè)不算錯(cuò)誤但很影響效率的事情默認(rèn)ALL_BUILD是順序編譯還是并行編譯取決于VS設(shè)置。在VS菜單里“工具–選項(xiàng)–項(xiàng)目和解決方案–VC項(xiàng)目設(shè)置”中把“最大并行項(xiàng)目生成數(shù)”調(diào)到4或更高編譯速度會明顯提升。如果內(nèi)存只有8G建議保持默認(rèn)防止多個(gè)編譯進(jìn)程同時(shí)打爆內(nèi)存。4.3 INSTALL項(xiàng)目與產(chǎn)物目錄ALL_BUILD編譯成功后找到INSTALL項(xiàng)目右鍵生成。這個(gè)項(xiàng)目不會產(chǎn)出新的功能庫而是把編譯結(jié)果統(tǒng)一復(fù)制到CMake里設(shè)置的安裝目錄。默認(rèn)安裝路徑是D:/opencv-3.3.0-build/install。生成完成后目錄結(jié)構(gòu)應(yīng)該是install ├── include │ └── opencv2 └── x64 └── vc14 ├── bin │ ├── opencv_world330.dll │ └── opencv_world330d.dll └── lib ├── opencv_world330.lib └── opencv_world330d.libvc14這個(gè)目錄名很關(guān)鍵它代表Visual Studio 2015的VC工具集版本。如果你后面用VS2015建立測試工程庫目錄要指到這里。另外驗(yàn)證一下bin下有沒有opencv_version.exe有的話直接命令行執(zhí)行opencv_version.exe輸出3.3.0就說明庫本身沒問題。接下來才有資格談驗(yàn)證contrib擴(kuò)展。5. 用SIFT在VS2015里驗(yàn)證contrib是否真的生效5.1 新建工程和目錄配置打開VS2015新建一個(gè)C控制臺工程。剛創(chuàng)建的時(shí)候默認(rèn)可能是Win32平臺這一步必須改。點(diǎn)菜單“生成–配置管理器”在“活動解決方案平臺”里選擇x64如果沒有就先新建一個(gè)x64平臺。否則后面鏈接時(shí)會報(bào)“這是無效的Win32應(yīng)用程序”或“無法加載DLL”的錯(cuò)。然后設(shè)置工程屬性配置項(xiàng)填寫內(nèi)容C/C – 常規(guī) – 附加包含目錄D:/opencv-3.3.0-build/install/include鏈接器 – 常規(guī) – 附加庫目錄D:/opencv-3.3.0-build/install/x64/vc14/lib鏈接器 – 輸入 – 附加依賴項(xiàng)Release下填opencv_world330.libDebug下填opencv_world330d.libDebug和Release的庫要匹配使用。如果把Debug工程鏈接Release版的opencv_world330.lib多數(shù)情況能跑但調(diào)試時(shí)變量監(jiān)視可能不正常如果Release工程鏈接了d版本庫運(yùn)行時(shí)會因?yàn)檎也坏秸{(diào)試版DLL失敗。所以最簡單的方法是OpenCV編譯時(shí)把兩者都編出來VS里切一次Debug生成ALL_BUILD和INSTALL再切一次Release生成這樣lib目錄下兩個(gè)文件都在。5.2 測試代碼與NONFREE雙保險(xiǎn)在工程里添加一個(gè)main.cpp內(nèi)容如下#define OPENCV_ENABLE_NONFREE #include opencv2/opencv.hpp #include opencv2/xfeatures2d.hpp #include iostream int main() { cv::Mat gray cv::imread(D:/test.jpg, cv::IMREAD_GRAYSCALE); if (gray.empty()) { std::cout image not found std::endl; return -1; } auto sift cv::xfeatures2d::SIFT::create(); std::vectorcv::KeyPoint keypoints; cv::Mat descriptors; sift-detectAndCompute(gray, cv::noArray(), keypoints, descriptors); std::cout keypoint count: keypoints.size() std::endl; std::cout descriptor size: descriptors.rows x descriptors.cols std::endl; return 0; }注意第一行#define OPENCV_ENABLE_NONFREE必須出現(xiàn)在所有OpenCV頭文件之前。雖然真正的“啟不啟用”在CMake階段就已經(jīng)決定但這個(gè)宏會在頭文件層面強(qiáng)制展開非自由算法的聲明相當(dāng)于雙保險(xiǎn)。如果你在CMake階段沒有勾選OPENCV_ENABLE_NONFREE運(yùn)行時(shí)SIFT會直接報(bào)“該算法受專利保護(hù)當(dāng)前構(gòu)建已禁用”程序返回空特征點(diǎn)。出現(xiàn)這種情況不要改宏回到CMake GUI勾上選項(xiàng)重新編譯OpenCV才是正解。5.3 運(yùn)行結(jié)果與常見報(bào)錯(cuò)排查編譯通過以后運(yùn)行程序前要確保opencv_world330.dll能被找到。最簡單的辦法是把install/x64/vc14/bin加進(jìn)系統(tǒng)環(huán)境變量Path然后重啟VS。不想改全局環(huán)境變量的話也可以把DLL直接復(fù)制到exe所在目錄。如果程序輸出keypoint count: 幾千這樣的信息說明contrib擴(kuò)展真的生效了。哪怕只有幾十個(gè)特征點(diǎn)只要數(shù)量不是0都說明SIFT模塊已被正確編譯進(jìn)OpenCV庫。常見報(bào)錯(cuò)我再列一張表現(xiàn)象原因處理編譯時(shí)報(bào)找不到xfeatures2d.hpp工程包含目錄沒配或者CMake階段contrib沒參與構(gòu)建檢查包含目錄和CMake模塊列表鏈接時(shí)報(bào)LNK2019 unresolved external symbolworld庫沒鏈接上或者鏈接的是非world庫但沒把contrib模塊的lib逐個(gè)寫上回到屬性頁確認(rèn)附加依賴項(xiàng)只有opencv_world330.lib運(yùn)行時(shí)提示“算法已禁用”CMake階段沒勾選OPENCV_ENABLE_NONFREE重新CMake并編譯OpenCV運(yùn)行時(shí)提示DLL缺失bin目錄不在Path中或dll版本是Debug/Release不匹配添加bin到Path或拷貝DLL到exe目錄6. 讓配置環(huán)境可復(fù)用的一些習(xí)慣6.1 環(huán)境變量建議只加局部很多教程讓你一上來就把install/x64/vc14/bin塞進(jìn)系統(tǒng)Path我建議換個(gè)思路。如果你的電腦上同時(shí)裝了OpenCV 3.x和OpenCV 4.x或者同時(shí)有VS2015和VS2019編譯的庫全局Path一加測試程序運(yùn)行時(shí)就會加載錯(cuò)DLL表現(xiàn)是程序啟動后馬上崩潰或者明明調(diào)用了最新庫卻報(bào)找不到新函數(shù)。我自己的習(xí)慣是在VS工程屬性里設(shè)置“調(diào)試–環(huán)境”或者寫一個(gè)setenv.bat腳本在編譯運(yùn)行前臨時(shí)把bin加進(jìn)Path。這樣不同項(xiàng)目可以用不同OpenCV版本互不干擾。發(fā)布程序時(shí)再手動把需要的DLL放進(jìn)exe同目錄。6.2 保留install目錄比重新編譯更省事OpenCV源碼編譯這件事難點(diǎn)全在configure階段和第三方依賴下載一旦成功install目錄就是一套完整可用的SDK。重裝系統(tǒng)以后只要你的VS2015還在直接把install目錄拷到新機(jī)器然后在工程屬性里指過去就行不需要重新走一遍CMake和編譯。如果用Docker類隔離環(huán)境或者項(xiàng)目遷移建議把opencv-3.3.0、opencv_contrib-3.3.0和opencv-3.3.0-build三個(gè)目錄整體打包存檔。以后任何人想復(fù)現(xiàn)這個(gè)環(huán)境打開CMake GUI時(shí)只需要重新填路徑其余配置全部能從build目錄的CMakeCache里恢復(fù)比對著文檔手點(diǎn)快得多。最后再多說一句如果你也被卡在某個(gè)奇怪的編譯錯(cuò)誤上不要急著改代碼先把CMake輸出窗口或者VS錯(cuò)誤列表里第一條紅色信息完整看一遍。老版本OpenCV的坑多數(shù)不是算法代碼的問題而是路徑、版本、網(wǎng)絡(luò)下載這三件事。把這三件事理順這篇配置流程基本就能一遍過。