opencv交叉編譯生成靜態庫,編譯demo

因工作需要,需在marvell平臺上交叉編譯出一個靜態鏈接opencv庫的可執行文件,只調用了其中的imread,resize等少數幾個函數。所以我最終只交叉編譯了一部分的鏈接庫,video相關的基本都沒有編譯,這個後續也會描述到。因爲是初次接觸opencv,之前交叉編譯的經驗也都沒記錄下來,特此留存,便於後續查閱。
我將最終要實現交叉編譯的過程分成四個階段來執行,因工作中沒有整片時間,拆成小目標容易分階段完成任務。
  • 在linux上安裝opencv。
  • 編譯源文件,並生成靜態鏈接到opencv和其他庫文件的linux端的可執行文件。
  • 交叉編譯opencv生成Marvell端的可執行文件,以及opencv之外的依賴庫和部分opencv依賴的庫
  • 編譯源文件,並生成靜態鏈接到交叉編譯的opencv庫和其他交叉編譯的庫的Marvell端的可執行文件
  • 階段一、在linux上安裝opencv。
    • 我的安裝環境是 virtualbox虛擬機,ubuntu 14.04- 64bit
    • 這步操作簡單,我是參考如下官方鏈接:https://docs.opencv.org/master/d7/d9f/tutorial_linux_install.html
      • 下載解壓源代碼到本地後,創建一個build目錄,到該目錄下執行cmake的操作。
        • cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
        • 注意結尾有 .. ; 即上級目錄
      • 然後直接按照要求make即可。
      • 如果按照說明操作,默認可執行文件安裝在/usr/local/bin , 鏈接庫在 /usr/local/lib,但默認應該是生成 .so
      • 如果要直接編譯靜態庫,建議安裝cmake-gui,指定了源代碼目錄和build目錄後,把configure之後生成的 BUILD_SHARED_LIBS選項去掉,然後才能在/usr/local/lib 下 生成libopencv_xxx.a,這也是爲下一步做準備
      • 關於靜態編譯這篇文章也可參考:http://blog.csdn.net/xdonx/article/details/38871659
        • 在 cmake時 使用 -DBUILD_SHARE_LIBS=OFF選項應該也是可以的,但我沒嘗試過。
    • 我下載的是3.3.0版本的源代碼,在如下的github鏈接裏,csdn的資料裏應該也是可以搜到的。
  • 階段二、編譯源文件,並生成靜態鏈接到opencv和其他庫文件的linux端的可執行文件。
    • 首次完成opencv的安裝建議執行 ldconfig 
    • 這步常規是比較簡單的,但就是需要注意編譯選項,包括  1、要包含的頭文件,-I。2、要鏈接的庫所在的文件夾,-L。3、要鏈接的庫-l。
      • 使用pkg-config opencv --cflags --libs 命令,可以提供編譯opencv時,目前opencv所有的上述所需的編譯選項。我本地執行的效果如下
        • -I/usr/local/include/opencv -I/usr/local/include -L/usr/local/lib -lopencv_dnn -lopencv_ml -lopencv_objdetect -lopencv_shape -lopencv_stitching -lopencv_superres -lopencv_videostab -lopencv_calib3d -lopencv_features2d -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_video -lopencv_photo -lopencv_imgproc -lopencv_flann -lopencv_core
    • 但是直接使用
      • g++ test.cpp -static `pkg-config opencv --cflags --libs`
      • 會出現非常多的鏈接未定義錯誤,原因是 opencv庫其實還依賴了很多的其他庫,要引用opencv的常規庫,必須也鏈接其對應的依賴,而這些需要手動添加。最常見的就是 -lpthread
    • 直接使用尋找依賴庫,然後一個一個手動加上顯然不是合理的做法。我在網上找了下資料。
      • opencv-3.3.0/build/unix-install/opencv.pc的文件,其中包含了要鏈接的內容。
        • Libs.private: -L${exec_prefix}/share/OpenCV/3rdparty/lib -littnotify -llibprotobuf -llibwebp -lIlmImf -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo -lpango-1.0 -lfontconfig -lgobject-2.0 -lglib-2.0 -lfreetype -lgthread-2.0 -L/usr/lib/x86_64-linux-gnu -lpng -lz -ltiff -ljasper -ljpeg -ldc1394 -lavcodec -lavformat -lavutil -lswscale -lstdc++ -ldl -lm -lpthread -lrt
      • 後半段內容應該能滿足我的需求。但需要注意對 -L目錄進行調整,同時可以去掉一些你不需要的 鏈接選項
    • 添加到上述g++ test.cpp -static `pkg-config opencv --cflags --libs`後面,果然把需要的依賴庫都加進來了,但是此時仍然可能在你的環境下出現錯誤,因爲你可能在本地沒有安裝過上述的依賴庫。上述的依賴庫一部分是集成在opencv裏面,在安裝時就編譯了的,另一部分則需要依賴本地。這裏我就不多介紹,可自行上網搜索對應的鏈接庫需要如何安裝。
    • 介紹一個過程中讓我碰到較多難題的鏈接庫 ljbig
      • 我用 sudo aptitude search jbig 是可以找到jbig的,但是應該只有動態鏈接庫,想靜態鏈接到 .a時在默認路徑下找不到。
      • 從https://github.com/mvanderkolff/jbigkit-packaging 下載對應package到本地.
      • 編譯後(make即可),把生成的 libjbig.a 拷貝到 /usr/lib/x86_64-linux-gnu 目錄,讓靜態編譯的時候可以鏈接到。
      • 拷貝後 sudo ldconfig,將新安裝的 庫文件導入ld.so.cache.
    • 我最終執行編譯源文件的命令如下,參考了opencv中編一個demo時的編譯選項和上面的鏈接選項,因只有單個文件,就沒引入Makefile
      • g++test.cpp -static -L/usr/local/lib-L/usr/local/share/OpenCV/3rdparty/lib -lopencv_dnn -lopencv_ml-lopencv_objdetect -lopencv_shape -lopencv_stitching -lopencv_superres-lopencv_videostab -lopencv_calib3d -lopencv_features2d -lopencv_highgui-lopencv_videoio -lopencv_imgcodecs -lopencv_video -lopencv_photo-lopencv_imgproc -lopencv_flann -lopencv_core -littnotify -llibprotobuf-llibwebp -lIlmImf -lgtk-x11-2.0 -lgdk-x11-2.0 -lgio-2.0 -lpangoft2-1.0 -lpangocairo-1.0  -lcairo -lpango-1.0 -lfontconfig-lgobject-2.0 -lglib-2.0 -lfreetype -lgthread-2.0 -lpng -lz -ltiff -ljasper-ljpeg -ldc1394 -lavcodec -lavformat -lavutil -lswscale -lstdc++ -ldl -lm-lpthread -lrt -lz  -llzma-ljbig

    • 這樣就完成了第二步,靜態編譯源文件。
    • 會出現一個warning
      •  warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
      • 但不影響可執行文件執行,故而暫時忽略。
  • 階段三、交叉編譯opencv生成Marvell端的可執行文件,以及opencv之外的依賴庫和部分opencv依賴的庫
    • 該階段主要包含兩塊,一是opencv的交叉編譯,二是其他依賴庫的交叉編譯。
    • 1、我先做了一步簡化,就是因爲我其實只用到opencv裏面的imread,resize等少數幾個函數,故而顯然不需要將上述所有內容作爲編譯選項。有一部分opencv的庫可以裁剪,這些庫對應的依賴庫也就可以裁剪。我嘗試逐步去掉鏈接選項,看是否會出現未定義的問題,裁剪最終得到的命令如下
      • g++ test.cpp -static -L/usr/local/lib-L/usr/local/share/OpenCV/3rdparty/lib -lopencv_imgcodecs -lopencv_photo -lopencv_imgproc -lopencv_core-littnotify -llibwebp -lIlmImf  -lpng-ltiff -ljasper -ljpeg  -ldl-lpthread  -lz  -llzma -ljbig
      • 實際只需要鏈接上面這些庫即可。這樣後續對依賴庫的交叉編譯工作其實就減輕了一大半。,然後在對opencv進行交叉編譯時,也可以額把上述沒有涉及到庫不進行編譯,也可縮短編譯時間和出問題時的排查時間。
    • 2、交叉編譯opencv過程我主要參考了下面這篇博文,基本覆蓋了我遇到的問題
      • http://blog.csdn.net/gatieme/article/details/49080355
      • 對博客中提到的內容,我在這裏也做一下簡述。主要分爲以下6個步驟
      • 1)使用Cmake-gui,選擇源碼目錄和build目錄,這點與安裝opencv時相同。
      • 2)configure生成Makefile時不能選擇默認項,要選擇第四項“ specify options for cross-compiling” 以配置交叉編譯的選項
      • 3)選擇交叉編譯工具鏈裏的C編譯工具,C++編譯工具,並選擇工具鏈所在的路徑。程序模式注意選擇默認項,部分博客說需要修改爲只是 Search in Target Root,實測會出現如下錯誤打印,即找不到部分編譯工具,因爲 交叉編譯工具鏈目錄下只包含了一部分交叉編譯工具,一些默認工具,比如Make,還是使用系統的,如果不把系統路徑納入編譯工具查找路徑,自然會出現查找失敗的情況。
        • CMakeError: CMake was unable to find a build program corresponding to"xxx".

      • 4)點擊Configure,即可生成配置內容。1)我去掉了一些不相關的video的庫的編譯。2、注意同樣需要去掉BUILD_SHARED_LIBS才能生成靜態庫。3)注意修改默認安裝路徑,因爲默認會生成到 /usr/local 這樣就跟我們安裝在linux上的opencv重疊了,可能會出現問題,故而我在這裏另外指定了一個路徑,用於存放生成產物。但我這步的操作好像實際並沒有生效。但不影響,後續產物還是會生成在build目錄下,-L鏈接進來即可。
        • 注意Build選項中需要用到的依賴庫都必須要勾選上,包括下表中的BUILD_JASPER,BUILD_OPENEXR等等
      • 5)Generate就會生成Makefile,然後執行Make即可。
      • 6)到對應目錄下執行make最後會出現pthread和dl相關的庫函數未定義的問題,因爲編譯時沒有加上這些庫函數的鏈接參數,需要在build目錄下的CMakeCache.txt文件中配置CMAKE_EXE_LINKER_FLAGS的參數,加上 -lpthread -ldl。就可以解決未定義錯誤的問題。
      • 博客中最後的問題我未遇到。但我遇到了下面這個問題。
        • libopencv_imgcodecs.so:undefined reference to `png_init_filter_functions_neon'
        • 是說libpng裏面的一個函數未定義,懷疑是開源庫的問題。如下網頁解決了該問題。
        • 我根據其patch,修改了我這邊的pngpriv.h文件,重新Make即解決。在如下目錄opencv-3.3.0/3rdparty/libpng。find一下即可。
      • 最終生成opencv_traincascade的可執行文件在/build/bin目錄下。
    • 3、交叉編譯opencv的依賴庫
      • 簡單瞭解一下opencv的源碼目錄框架即可發現,3rdparty目錄下就已經包含了opencv自身需要的第三方的庫,經對比,-littnotify -llibwebp -lIlmImf  -lpng-ltiff -ljasper -ljpeg 都是opencv源碼包裏面已經包含了的庫。(p.s. 其實opencv用到的這些庫都可以瞭解一下,作爲拓展視野,比如ittnotify 我發現就是一個檢測內存泄露的庫,可能會在其他就會有用到
      • libz,libpthread的庫因爲比較通用,在交叉編譯工具鏈的默認工具庫裏就有,後續編譯時,鏈接進來即可。
      • lzma庫後來發現其實並未用到。
      • jbig庫在上面已經編譯過。將能找到的編譯工具,gcc,ar,等等都改爲我所希望的aarch64-marvell-linux-gnu-系列即可。
  • 階段四、編譯源文件,並生成靜態鏈接到交叉編譯的opencv庫和其他交叉編譯的庫的Marvell端的可執行文件
    • 因之前對編譯的核心過程理解不深刻(其實現在理解也不深刻 - -!),在這步上我出了不少問題。
    • 首先的問題就是我在交叉產物的目錄(就是上面cmake-gui中指定的)下找不到生成的頭文件,最終我使用了原來在linux上安裝opencv時,在/usr/local/include目錄下的頭文件,因爲我猜想這塊頭文件應該是一致的,事實證明猜想正確。
    • 然後我使用-L選項把所有用到的Marvell平臺的依賴庫的位置都包含進來
    • 最終版本的編譯執行命令如下。
      • aarch64-marvell-linux-gnu-g++ test.cpp -static -I/usr/local/include  -L/opt/Marvell/marvell_i686_gnu/aarch64-linux-gnu/libc/lib -L/home/xxxx/package/build_dir/lib -L/home/xxxx/package/build_dir/3rdparty/lib -lopencv_imgcodecs -lopencv_photo -lopencv_imgproc -lopencv_core -llibwebp -lIlmImf -lpng -ltiff -ljasper -ljpeg -ldl -lpthread -lz -ljbig
      • -L/opt/Marvell/marvell_i686_gnu/aarch64-linux-gnu/libc/lib 是我的交叉編譯工具鏈的已經經過交叉編譯的庫所在的目錄
      • /home/xxxx/package/build_dir就是我指定的編譯產物目錄
    • 頭文件和鏈接庫的邏輯關係其實還需要再捋順一些。
  • 如上便是交叉編譯opencv生成靜態庫後,我嘗試編譯一個最基本的應用的一個過程。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章