メッセージから逆引きしてエラーの原因を特定する記事

# はじめに
- 本記事は[OpenCV Advent Calendar 2021](https://qiita.com/advent-calendar/2021/opencv) の9日目の記事です
    - 昨日はsuo-takefumiさんの[100行で作るAR](https://qiita.com/suo-takefumi/items/231a6f97cd880fe629a5)でした。
    - 凹みや穴を重畳するのは良いデモでしたね
- 本日の記事はOpenCV使用時やビルド時に出てくるエラーメッセージについてです。
    - OpenCVは、意味不明なエラーに遭遇することがあります。
- 公式のGithubのissueを眺めていると、明後日な投稿が多数あるのですが、そのうち「一目で原因がわかる」メッセージ達を集めてみました。

# ビルド編
## `c++: internal compiler error: Segmentation fault (program cc1plus)`
 - 原因:ビルドマシンのメモリ量が足りない
 - 対策：もっとメモリを積むか、`-j 1` を明示的につける
 - x86マシンだと潤沢にメモリを詰めるが、Raspberry Pi/Jetsonなどの組み込みボードだとメモリが枯渇することがたまに起きる。
 - 「メッセージの通りコンパイラのバグだ！」「OpenCVのバグだ！」と騒ぐ人をたくさん見てきたけれど、メッセージの意図をちゃんと読む必要がある
    - 通常、コンパイル時間を短くするために並列ビルドを行い、`-j 4`などして4コアで並列してビルドすることがある
    - このとき、メモリ使用量は並列処理数分だけ多く利用することになる
    - また、OpenCV内の一部のソースコードたちも、マクロを多用していたりすると、単体のコンパイルでもメモリ使用量が跳ね上がったりする。
    - メモリの使用量が逼迫している場合、GCC内でSegmentation faultが起きたり、カーネルにプロセスをkillされたりする
 - という訳で、前述の対策に行き着く。
 - また、`ccache`を使うのも、並列ビルドを可能にしながらもメモリ使用量を抑える良い手段（その代わりストレージが必要になる)

## Duplicated modules NAMES has been found
- メッセージ文中のNAMESには特定のモジュールの名前が入る
- 原因:contrib付きでビルドする際に、本体側が3.4系列、contrib側がmasterだと発生する
- 対策:本体側、contrib側で同じブランチをチェックアウトする
- opencv_contribは、いわばOpenCVの2軍とも言うべき存在でまだ荒削りなmoduleだったり使用頻度の低いmoduleがcontribに格納される
- ときに、contribから本体は「昇格」があったり、逆の「降格」があったりする。その際、本体側とcontrib側でbranchの整合性が取れてないと、同じ名前のmoduleが両方に存在してしまう
- 結果、前述のエラーメッセージが以下のように発生する

```
core
> cudaarithm
> cudabgsegm
> cudacodec
 : (以下続く)
```

- 3.4系列まではCUDA関連のモジュールは本体側にあったが、4系列でcontribに送られた。
- このため、このエラーで目にするモジュール名はCUDA関連のモジュールであろう。
- ただし、エラーが特徴的なので、すぐに見抜けるはず

## `fatal error: boostdesc_bgm.i: No such file or directory`
- make時に発生する
- 原因:https通信に問題あり
- `xfeatures2d` モジュールのcmake時に、バックグラウンドでソースコード(分類器)のダウンロードが行われる
- このとき、CMake内製の通信モジュールでhttps通信が行われる
- https通信に失敗しうる原因は主に2つ
    - 1つはビルド時の環境がproxyなどを利用しており、一部のhttpsサイトを遮断している場合
    - もう1つは、ソースコードからcmakeをビルドして、その際cmakeのbootstrapオプションに`--system-curl`をつけ忘れた場合
    - いずれの場合もhttps通信に失敗し、ファイルがダウンロードされない。
    - そしてダウンロードされてないファイルを参照するため、前述のエラーが発生する
- OpenCVのバグではないので、自身のネットワーク環境を調べよう
- なんでこういう仕組みになってるのかはわからないけれど、もしかしたらライセンス関係でこうなってるのやもしれぬ（未調査）

## `nvcc fatal Unsupported gpu architecture compute_xx`
- 末尾の`xx`には数字が入る
- 原因:CUDAのバージョンが希望のCCをサポートしていない
- 対策:新しいGPUを使いたいなら新しいCUDAにする。

## `Error generating /xxx/modules/core/CMakeFiles/cuda_compile_1.dir/src/cuda/./cuda_compile_1_generated_gpu_mat.cu.o`
- 原因:ディスク容量不足
- 対処方法:/tmp/パーティションが載ってるディスクの容量を空ける
- CUDA関連のコードは/tmp/以下に一時的なファイルを生成する
- このファイルはときにGBオーダーのサイズになることがある
- Jetson Nanoを始めとして、SDカードの容量をケチったりしてると、/tmp/ のパーティションがいっぱいになることも多々見かける。

## `Required baseline optimization is not supported: VFPV3`
- 原因:Aarch64上でのビルドで、`-DCPU_BASELINE=VFPV3`をつけている
- 対処方法:`CPU_BASELINE`オプションは空にする
- `CPU_BASELINE`オプションはCMake時に渡す
- Aarch64で使えるオプションは現在(2021/12/1時点, OpenCV 3.4.16, 4.5.3)使えるのは`NEON`と`FP16`のみ。
    - Armv7 では`VFPV3`も使えたが、`VFPV3`は`NEON`のサブセットとでも言うべき拡張であり、`NEON`がAarch64 (Armv8)で標準命令セットに含まれた以上、`VFPV3`だけ個別に指定する意味は無い
    - もっと言えば、`FP16`命令も標準命令に含まれているので、Aarch64は`CPU_BASELINE`オプションを使う場面は無い
    - ただ、Apple M1を始め、オイシイ拡張命令がどんどんアクセスできるようになってるので、いずれOpenCVで拡張命令を使った実装がされる可能性が高い。
    - なので、`CPU_BASELINE`オプションを指定して意味がある日が来るかもしれない。

# 実行時編
## unresolved external "__declspec(dllimport) bool cv::__termination in DllMain"
- 筆者の[過去記事](https://qiita.com/tomoaki_teshima/items/6616670e53979155c58e)参照
- 原因はOpenCV 4.0.0固有の既知のバグ
- OpenCV 4.0.0だけで起きる。3系列では起きないし、4.0.1ではFIXが含まれている
- 迂回策として、 `cmake -DOPENCV_SKIP_DLLMAIN_GENERATION=ON ...` というように`OPENCV_SKIP_DLLMAIN_GENERATION`オプションを使う方法もある
- このページに書いてある現象のうち、この現象だけはOpenCVのバグである。あとは環境か使い方の問題であることに注意。

## `OpenCL error CL_OUT_OF_RESOURCES (-5) during call: clEnqueueNDRangeKernel`
- 環境によってはわんさかと遭遇するメッセージ
- これは`error`という文言を含んでいるが、エラーではない。OpenCLから出てくるメッセージ
- 具体的には、使用しているOpenCLデバイスに対して、可能な容量以上の計算を投げると、OpenCL側でエラーを挙げる。このときコンソールに表示される画面が前述のメッセージ
- 一方で、OpenCV内ではOpenCL実行がコケた場合、自動的にCPU実装に切り替える[^automatic-switch]ので、見た目にはエラーに見えない。

[^automatic-switch]: 唯一`rgbd`モジュールのKinect Fusion関係のコードは、本エラーを正しくハンドリングしない。ぴえん。

## `OpenCV Error: Assertion failed (size.width>0 && size.height>0)`
- 原因:ファイルのオープンに失敗している
- 解説:OpenCVでは、ファイル名を間違えていたり、存在しないファイルをオープンしようとしたり、またはパーミッションでオープンできなかった場合など、正しくファイルを開けなくても、実行時エラーを「投げない」
- 代わりに、ユーザはファイルが正しくオープンできたかどうか、`.empty()`メソッドを使って確認することを求められる
- ファイルのオープンに失敗して、空っぽの`Mat`であることを確認せずにOpenCVのAPIに投げると、前述のような、様々な実行時エラーが投げられます。
- 直接の起因とエラーメッセージが対応してないのでわかりにくいですが、原因はファイルのオープンです。
- また、APIによって、Assertionが様々ですので、前述のメッセージ以外のメッセージが出ることがあります。気をつけましょう。

# 番外編
## 野良ビルドを多用した場合
- タイトルと趣旨が反するのですが、「不思議なことが起きたときの原因」の話です。
- 当時私が突き当たった環境はWindows 7、MSVC2015、OpenCVは3.0かそのあたりの話だったと思います。
- 現象としてはアクセス違反、確保した領域外にアクセスしていました。
  - が、状況をよくよく調べると、「画像をopenしてロードする際、コンストラクタを通らずにいきなりallocateされてないメモリ領域に書き込もうとしてアクセス違反」という意味不明な状況でした。
  - また、`imread`関数にわたすパラメータに依らずアクセス違反が発生しており、果たして原因は何なのか、途方にくれていました。
- 紐解けば簡単な話で、ビルド時に使ったlibファイルと、実行時に読み込まれるdllファイルが、違うバージョンだったのです
    - 厳密にはバージョンはマイナーバージョンまで一致していたのですが、微妙なビルドオプションが違いました。
    - 下手にマイナーバージョンが合っていたため、明示的にエラーが発生せず、関数callがへんてこな場所から開始していたためでした。
- 野良ビルド、気をつけましょう。

# さいごに
- OpenCVのエラーメッセージは残念ながらお世辞にもわかりやすいとは言えません。
- このページにたどり着いた人が少しでも時間の無駄を減らせれば、と思います
- 明日は@fukushima1981先生の投稿で、執筆時点でのタイトルは「[OpenCVによる特異値分解と最小二乗法](https://qiita.com/fukushima1981/items/643aa4ced546dca1fe2a)」です。楽しみですね！