LoginSignup
7
1

More than 5 years have passed since last update.

OpenCV ジェンガ

Last updated at Posted at 2018-12-07

はじめに

  • これは、OpenCV Advent Calendar 2018 8日目の記事です。
  • 関連記事は目次にまとめられています。
  • なお、本記事は筆者個人の意見であり、筆者の所属組織とは無関係です。

TL;DR

  • OpenCVのDLLの依存関係は複雑なので、マシン間でDLLをコピーする場合は一式コピーした方が無難です

導入

  • 今年の筆者のツイートのうち、2番目に多くのイイねを受けたのが以下のツイートでした。1
  • OpenCVをビルドしたマシンとは別のマシンにDLLをコピーする際、面倒臭がって一部のDLLだけコピったら何度も"DLL Not Found"に遭遇して辟易したので、先のツイートに辿り着くわけです。
  • とはいえ、依存関係どうなってるんだろう?と思って、可視化してみました。

調べ方

  • 以下のシェルスクリプトをGit Bashで動かして調べました。
#!/bin/bash
DUMPBIN=/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio\ 14.0/VC/bin/dumpbin.exe
TARGET_DIR="/c/work/sdks/opencv/x64/vc14/bin"

DLL_LISTS=`ls ${TARGET_DIR}/*[^d].dll`

for TARGET_FULLPATH in ${DLL_LISTS}
do
    TARGET_DLL=`basename ${TARGET_FULLPATH}`
    DEPEND_LIST=`"${DUMPBIN}" -dependents ${TARGET_FULLPATH} | grep -e opencv -e MSVC -e VCRUNTIME -e cudart | egrep -v '\/' | tr -d [:blank:]`

    for var in ${DEPEND_LIST}
    do
        echo "${TARGET_DLL}->$var" | sed -e "s/.dll/_dll/g"
    done
done
  • TARGET_DIRにあるdll全ての依存ライブラリを調べ、OpenCVとCUDAに関係ありそうなものだけ抽出しています。 dumpbinはめちゃくちゃ便利なツールなのでオススメです。

  • これで出てきた情報に、適当にヘッダとフッタを付けたのが以下のようなテキストです。

digraph
{
rankdir=LR
opencv_calib3d400_dll->opencv_features2d400_dll
opencv_calib3d400_dll->opencv_flann400_dll
opencv_calib3d400_dll->opencv_imgproc400_dll
opencv_calib3d400_dll->opencv_core400_dll
opencv_dnn400_dll->opencv_imgproc400_dll
opencv_dnn400_dll->opencv_core400_dll
opencv_features2d400_dll->opencv_flann400_dll
opencv_features2d400_dll->opencv_imgproc400_dll
opencv_features2d400_dll->opencv_core400_dll
opencv_flann400_dll->opencv_core400_dll
opencv_gapi400_dll->opencv_imgproc400_dll
opencv_gapi400_dll->opencv_core400_dll
opencv_highgui400_dll->opencv_videoio400_dll
opencv_highgui400_dll->opencv_imgcodecs400_dll
opencv_highgui400_dll->opencv_imgproc400_dll
opencv_highgui400_dll->opencv_core400_dll
opencv_imgcodecs400_dll->opencv_imgproc400_dll
opencv_imgcodecs400_dll->opencv_core400_dll
opencv_imgproc400_dll->opencv_core400_dll
opencv_ml400_dll->opencv_core400_dll
opencv_objdetect400_dll->opencv_calib3d400_dll
opencv_objdetect400_dll->opencv_features2d400_dll
opencv_objdetect400_dll->opencv_flann400_dll
opencv_objdetect400_dll->opencv_imgproc400_dll
opencv_objdetect400_dll->opencv_core400_dll
opencv_photo400_dll->opencv_imgproc400_dll
opencv_photo400_dll->opencv_core400_dll
opencv_stitching400_dll->opencv_calib3d400_dll
opencv_stitching400_dll->opencv_features2d400_dll
opencv_stitching400_dll->opencv_flann400_dll
opencv_stitching400_dll->opencv_imgproc400_dll
opencv_stitching400_dll->opencv_core400_dll
opencv_video400_dll->opencv_calib3d400_dll
opencv_video400_dll->opencv_features2d400_dll
opencv_video400_dll->opencv_flann400_dll
opencv_video400_dll->opencv_imgproc400_dll
opencv_video400_dll->opencv_core400_dll
opencv_videoio400_dll->opencv_imgcodecs400_dll
opencv_videoio400_dll->opencv_imgproc400_dll
opencv_videoio400_dll->opencv_core400_dll
}

解説

  • 各行依存関係を表し、->記号の左側のDLLが右側のDLLに依存することを表しています。
  • 以下の例だとopencv_calib3d400.dllopencv_features2d400.dllに依存することを意味します。
opencv_calib3d400_dll->opencv_features2d400_dll

可視化

  • さて、これをGraphvizを使って可視化してみましょう。

jenga_opencv_400.png

  • 図中の矢印は、依存する方から依存される方へ伸びています。
  • 思ったよりカオス!
  • 念の為SVG版も置いておきます。

観察

  • すべてのモジュールはcoreモジュールに依存する
    • 例外なく全てのモジュールからcoreモジュールへ矢印が到達します。coreモジュールの名前は伊達じゃないという訳ですね。
  • coreモジュールのみに依存するのはimgprocmlflann
    • coreモジュール以外に依存関係が無いモジュールを第二層とでも呼びましょうか。imgprocが入ってるのは何となく想定していましたが、まさかのmlモジュールも依存関係がありません。
    • また、mlモジュールに依存しているモジュールが存在しないのも特筆すべき点です。
  • それ以降
    • 以降、順番に依存しているモジュールをもとに第三層、第四層と決めていくと、
    • 第三層にはfeatures2ddnngapiimgcodecsphotoが含まれます
    • 第四層にはvideoiocalib3dが含まれます
    • 第五層にはvideohighguistitchingobjdetectが含まれます
  • 適当に「層」って名前を使いましたが、グラフ理論的には何かちゃんとした名前がありそうな気がします。知ってる人、教えて下さい。

CUDA 付きでビルドした場合

  • 念の為、CUDA付きでもビルドしてみました。
    jenga_opencv_400_cuda.png

  • よりカオス!

  • また、念の為contrib+CUDA付きのSVG版も置いておきますね。

観察

  • 通常版と違い、cudart64_100.dllへの依存が発生しています
  • これがCUDAのランタイムライブラリであり、CUDA付きでビルドしたOpenCVのDLL群はこのdllが必要になります。
  • また、ここで4日目の記事で言及したcudevモジュールについて解説します
  • cudevモジュールは、先程のグラフに出てきません。何故ならば
    • どのモジュールもcudevモジュールに依存しないし
    • どのモジュールもcudevモジュールに依存されないためです。
  • 念の為opencv_cudev400.dllだけdumpしてみましょう。
> dumpbin /dependents opencv_cudev400.dll
COFF/PE Dumper Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file opencv_cudev400.dll

File Type: DLL

  Image has the following dependencies:

    VCRUNTIME140.dll
    api-ms-win-crt-runtime-l1-1-0.dll
    KERNEL32.dll

  Summary

        1000 .data
        1000 .gfids
        1000 .pdata
        1000 .rdata
        1000 .reloc
        1000 .rsrc
        1000 .text
  • この通り、何故かOpenCVはおろかCUDA関連のDLLにも依存しません。
  Image has the following dependencies:
    VCRUNTIME140.dll
    api-ms-win-crt-runtime-l1-1-0.dll
    KERNEL32.dll
  • 実際このモジュールに入ってるヘッダファイル達が他のモジュールに取り込まれたりします。
  • なので、ビルド時にcudevモジュールが有効だとcoreモジュールがリンクしようとします。
  • なので、依存関係としてはcorecudevの向きに依存が発生します。
  • 4日目の記事ではcv::__terminationによって、cudevcoreの依存関係が無いのに、extern変数の参照で依存関係が発生し、ビルドに失敗していた訳です。
  • しかも、そもそも循環参照になるから依存関係があってはいけないデザインになってますね。
  • というか、ではヘッダファイルだけが必要なのであって別モジュールに分ける必要は無かったのでは?と疑問が絶えません。

まとめ

  • OpenCVのDLLは依存関係があるので、マシン間でDLLをコピーする場合は注意しよう!
  • 明日は@UnaNancyOwen先生の記事で、執筆時のタイトルは「cv::VideoCaptureのRealSense SDKサポートについて書くかもしれない」です。お楽しみに!

おまけ


  1. 参考までに、一番バズったのは、OpenCV 4.0-betaのリリースノートを深夜のテンションで和訳したツイートでした 

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1