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