MAC OSX下opencv 4的靜態庫編譯與連接/ compile and link opencv static library under OSX

我!終于!成功在MAC下面把opencv4程序靜態編譯起來了!
先上compile的命令:
g++ -std=c++11 main.cpp preprocess.cpp -framework Foundation -framework OpenCL -framework CoreMedia -framework AudioToolbox -framework CoreVideo -framework AVFoundation -framework CoreFoundation -framework CoreGraphics -I./include -L./lib -lopencv_videoio -lopencv_gapi -lopencv_features2d -lopencv_photo -lopencv_ts -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_imgproc -lopencv_core -lippiw -lippicv -ljpeg -llibpng -littnotify -lade -lIlmImf -lzlib -llibjpeg-turbo -lquirc -llibjasper -llibtiff -llibwebp -o main

有沒有發現有什么不對?沒錯就是掛在-L和-I前的一堆 -framework,微笑,因為MAC OS下自己有一堆的framework,opencv在MAC下編譯會依賴這些靜態庫,所以編譯的時候需要把他們一起連接上,這也是為什么即使把opencv的靜態庫全部連接上并整理好依賴關系還會出現
Undefined symbols for architecture x86_64:
"_CFDataGetBytePtr", referenced from:
CvVideoWriter_AVFoundation::writeFrame(_IplImage const) in libopencv_videoio.a(cap_avfoundation_mac.mm.o)
"_CFRelease", referenced from:
CvCaptureFile::~CvCaptureFile() in libopencv_videoio.a(cap_avfoundation_mac.mm.o)
CvCaptureFile::grabFrame() in libopencv_videoio.a(cap_avfoundation_mac.mm.o)
releaseCallback(void
, void const) in libopencv_videoio.a(cap_avfoundation_mac.mm.o)
"_CGColorSpaceCreateDeviceRGB", referenced from:
CvVideoWriter_AVFoundation::writeFrame(_IplImage const
) in libopencv_videoio.a(cap_avfoundation_mac.mm.o)
"_CGColorSpaceRelease", referenced from:
CvVideoWriter_AVFoundation::writeFrame(_IplImage const*) in libopencv_videoio.a(cap_avfoundation_mac.mm.o)
類似于這種錯誤(方便復制錯誤的旁友搜到這個帖子)
回歸正題

1. MAC OS下靜態編譯

參考https://shiffman.net/opencv/2011/01/23/how-to-build-opencv-static-libraries-mac-os-x/
這篇里的with_ffmpeg去掉的話就不能進行視頻處理,所以如果要處理視頻的話請看下面的更新。
其實就是在CMAKE里把BUILD_SHARED_LIBRARIES=OFF, CMAKE GUI的確很好用,安利。不過這樣編譯的靜態庫好像缺了一個libippicv.a,我是直接在opencv的安裝目錄下找到的,所以可能不用自己重新靜態編譯,直接下載的Opencv安裝目錄下的靜態庫也能用。

2. 靜態鏈接

首先把頭文件全部拷貝到當前目錄的include下,靜態庫全部拷貝到當前目錄的lib下。
因為opencv的靜態庫之間還有依賴性,所以直接一股腦放上去是不行的。在電腦里已經安裝了opencv的情況下可以參考 pkg-config --cflags --libs opencv4出來的動態庫的順序。
如果依然搞不清順序,可以查看undefined symbol的錯誤信息,比如“_WebPDecodeBGRAInto”, 利用 nm lib/*.a -A| grep "_WebPDecodeBGRAInto" 可以找到含有此標志的.a文件,其中U代表引用別的靜態庫此標志,D代表定義此標志(大概?不懂靜態庫編譯,從我的搜索結果來看好像是這樣)把D的文件放到U的后面就可以了,比如此處把-lwebp 放在-lopencv_imgcodecs的后面。
最后當你發現有的symbol在/usr/local/下所有的靜態庫文件都找不到的時候。。。。
谷歌會發現這個symbol原來在OSX的framework中,微笑.jpg。
于是我發現了-framework Foundation -framework OpenCL -framework CoreMedia -framework AudioToolbox -framework CoreVideo -framework AVFoundation -framework CoreFoundation -framework CoreGraphics,最后把這些framework都加上,終于大功告成啦!

(碎碎念:寫Opencv的程序花了一天,想compile成獨立的可執行程序斷斷續續花了三周真是遭不住。嘗試過動態連接+用install_name_tool 修改程序動態庫的路徑,opencv的動態庫是沒問題了,但是opencv_core依賴的一個openblasp庫不知道為什么即使打包在文件夾里依然會顯示no suitable image found, did find error... 大概意思好像是雖然有這個文件但是文件有問題?不合適,這真的不合適_> 有旁友動態連接成功了的話請告訴我怎么搞! 拜謝!)


更新(錯誤方法):

程序運行時發現video capture報錯無法運行,搜索之后發現是缺了ffmpeg的庫,系統自帶的ffmpeg有動態庫和靜態庫,但是opencv編譯時去掉了WITH_FFMPEG,沒去掉的話也是默認連接動態庫,因此在opencv的CMAKE里把ffmpeg相關的庫都修改成靜態庫:


image.png

重新編譯opencv,這時候make又會開始報Undefined symbol的錯誤,又要回過頭調查每個ffmpeg的庫依賴(MMP)


更更新:

上面的方法直接使用ffmpeg靜態庫的時候還不知道ffmpeg依賴libbz2和libiconv,libiconv的靜態庫標志和動態庫標志又不一樣,一個是_libiconv_open一個是_iconvopen,系統自己的ffmpeg里面的libavcodec又有一大堆依賴,糾結了很久libiconv怎么處理甚至還差點把電腦弄壞= =。
終于發現其實可以自己重新編譯一次ffmpeg,有把iconv去掉這一個選項!
所以步驟為

  1. 下載ffmpeg源碼并編譯
    ./configure --enable-static --disable-iconv --disable-gnutls --disable-libbluray --disable-x86asm --enable-avresample
    gnutls和libbluray一個沒找到靜態庫一個靜態庫有一堆依賴-皿-,系統自帶的ffmpeg里有他們不過ffmpeg的configure默認是disable的,加一個保險一點。 --disable-iconv和--enable-avresample是必需的因為默認Iconv會編譯進去以及不編譯avresample的靜態庫,但是這個庫又是opencv會引用的。
  2. 下載libbz2源碼并編譯
    因為這個靜態庫很好編譯所以我沒有嘗試ffmpeg編譯時能不能去掉這個庫, 查看了一下configure里沒有對這個庫disable的說明所以我猜可能是不能去掉的。
  3. 修改opencv CMAKE
    shared-library和之前一樣去掉?
    新的ffmpeg路徑:


    image.png

    這里雖然有libiconv的路徑,但是因為這個是ffmpeg的dependency不是opencv的dependency,所以后面compile的時候可以忽略這個庫,就不會發生symbol undifined的問題啦!
    要注意configure以后一定要往下拉看video IO模塊ffmpeg有沒有顯示yes:


    image.png

    這些版本其實都是動態庫的版本不用管
    generate并重新編譯opencv。
  4. 用新的opencv和ffmpeg靜態庫替代之前的庫, 在compile的時候添加依賴項然后就可以運行視頻處理了!
    可能需要添加一些ffmpeg的依賴庫,不過這個時候的依賴庫都是系統里可以很容易找到靜態庫的了,找不到的symbol可以用nm $(find /usr/ -type f -name "*.a") -A |grep "_aom_codec_av1_cx"這個命令行尋找需要的symbol在哪。
    最后的compile命令:
    g++ -std=c++11 main.cpp preprocess.cpp -lavformat -lavcodec -lavdevice -lavfilter -lavutil -lswresample -lavresample -lswscale -llzma -framework Security -framework VideoToolbox -framework CoreServices -framework Foundation -framework OpenCL -framework CoreMedia -framework AudioToolbox -framework CoreVideo -framework AVFoundation -framework CoreFoundation -framework CoreGraphics -framework Cocoa -framework Accelerate -framework QuartzCore -I./include -L./lib -lopencv_videoio -lopencv_gapi -lopencv_features2d -lopencv_photo -lopencv_ts -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_imgproc -lopencv_core -lippiw -lippicv -ljpeg -llibpng -littnotify -lade -lIlmImf -lzlib -llibjpeg-turbo -lquirc -llibjasper -llibtiff -llibwebp -lssl -lcrypto -lcharset -lbz2 -lxvidcore -o main
    新加的有ffmpeg的幾個庫,Security, VideoToolBox, CoreServices framework, 還有最后的ssl, crypto, charset, bz2, xvidcore庫,除了libbz2需要自己編譯靜態庫,別的都是系統里就有的。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。