OpenCV for Unityの使い方講座１　エラーハンドリング編

Unityの便利Assetである「OpenCV for Unity」を利用するにあたり初学者が躓きそうなポイントを解説していくシリーズ記事の第１弾です。

Assetの基本的なセットアップ方法などは[UnityでOpenCVを利用した顔検出・画像処理アプリ事始め](http://qiita.com/utibenkei/items/fb93623a7ccaabb342f2)を見てください。

##OpenCV for Unityのエラーログ（CVException）を確認するには一手間必要##

意外と知らない人が多いのですが、**OpenCV for Unityはデフォルト状態ではUnity側で呼び出されるOpenCVネイティブライブラリの内部で発生したエラーログがコンソールに出力されません。**
そのために使い初めのうちは困惑する方もいるようです。
私自身もMatの演算処理などで上手くいかない理由が分からずに少しの間ハマりました。（たいていはMatの型が違うことで発生することが多いです）
とにかくOpenCV for UnityではCVExceptionを補足したりコンソールに出力するためには一手間必要になります。

##Utils.setDebugメソッドの使い方##

OpenCV for Unityの**Utilsクラスにはエラーログの出力の有無を切り替えるためのsetDebugメソッド**が用意されています。
具体的には以下のコードが実装されています。

```
Utils.csより一部引用
        /**
        * Sets the debug mode.
        * <p>
        * <br>if debugMode is true, The error log of the Native side OpenCV will be displayed on the Unity Editor Console.However, if throwException is true, CvException is thrown instead of calling Debug.LogError (msg).
        * <br>Please use as follows.
        * <br>Utils.setDebugMode(true);
        * <br>aaa
        * <br>bbb
        * <br>ccc
        * <br>Utils.setDebugMode(false);
        * 
        * @param debugMode if true, The error log of the Native side OpenCV will be displayed on the Unity Editor Console.
        * @param throwException if true, CvException is thrown instead of calling Debug.LogError (msg).
        */
        public static void setDebugMode(bool debugMode, bool throwException = false)
        {
～以下略～
```

**つまりOpenCV for UnityではUtils.setDebug(true)であらかじめデバッグモードをONにしていないと、例外が発生したとしてもコンソールに出力されることはありません。**

以降からは[githubでも公開されているExamplesの中のMatBasicProcessingExampleのコード](https://github.com/EnoxSoftware/OpenCVForUnity/blob/57fab97beb195e63bed1f7e0c7db9ce609ff71d8/Assets/OpenCVForUnity/Examples/Basic/MatBasicProcessingExample/MatBasicProcessingExample.cs#L239-L328)を例にエラーハンドリングの具体的な方法を解説します。

###Utils.setDebug(true, false);でエラーログをコンソールに出力させる###
エラーが発生した際にコンソールにログを出力したい場合はこのように書きます。

```csharp:MatBasicProcessingExample.cs#L239-L328

            // 32F, channels=1, 3x3 //（Float型、1チャンネル、3x3のMatを作成する）
            Mat m1 = new Mat (3, 3, CvType.CV_32FC1);
            m1.put (0, 0, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);

            // 8U, channels=1, 3x3 //（byte型、1チャンネル、3x3のMatを作成する）
            Mat m2 = new Mat (3, 3, CvType.CV_8UC1);
            m2.put (0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

～中略～

            // CVException handling
            // Publish CVException to Debug.LogError.
            Utils.setDebugMode (true, false); //（デバッグモードON、例外スローOFF）

            Mat m3 = m1 / m2; // element type is different. //（Matの型が違う同士の演算なのでエラー発生。エラーログがコンソールに出力される）
            Debug.Log ("m3=" + m3); //（演算処理は失敗しているので中身のないMatが生成されている）

            Utils.setDebugMode (false); //（デバッグモードOFF）
～以下略～
```

コンソール出力結果

```
core::divide_12() : OpenCV(3.4.1-dev) C:\Users\xxxxx\Desktop\opencv\modules\core\src\arithm.cpp:683: error: (-5) When the input arrays in add/subtract/multiply/divide functions have different types, the output array type must be explicitly specified in function cv::arithm_op
```

###Utils.setDebug(true, true);でCVException例外をスローさせる###

自分で例外処理したい場合はこのように書きます。（第二引数をtrueにする）

```csharp:MatBasicProcessingExample.cs#L239-L328

            // 32F, channels=1, 3x3 //（Float型、1チャンネル、3x3のMatを作成する）
            Mat m1 = new Mat (3, 3, CvType.CV_32FC1);
            m1.put (0, 0, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);

            // 8U, channels=1, 3x3 //（byte型、1チャンネル、3x3のMatを作成する）
            Mat m2 = new Mat (3, 3, CvType.CV_8UC1);
            m2.put (0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

～中略～

            // Throw CVException.
            Utils.setDebugMode (true, true);　//（デバッグモードON、例外スローON）
            try {
                Mat m4 = m1 / m2; // element type is different. //（Matの型が違う同士の演算なので例外がスローされる。）
                Debug.Log ("m4=" + m4); //（演算処理は失敗しているので中身のないMatが生成されている）
            } catch (Exception e) {
                Debug.Log ("CVException: " + e);
            }
            Utils.setDebugMode (false); //（デバッグモードOFF）
～以下略～
```

コンソール出力結果

```
CVException: OpenCVForUnity.CvException: core::divide_12() : OpenCV(3.4.1-dev) C:\Users\satoo\Desktop\opencv\modules\core\src\arithm.cpp:683: error: (-5) When the input arrays in add/subtract/multiply/divide functions have different types, the output array type must be explicitly specified in function cv::arithm_op
```

このようにOpenCVのNative層で発生するCVExceptionのエラーログを確認することが可能です。
あとはキャッチしたエラーログをググったりして原因を特定するなどしてください。
オープンソースライブラリであるOpenCVのソースコードの該当行を直接確認することも有効です。
**とりあえず開発中は常にデバッグモードをONにしておいても問題ないと思います。**

##余談‐Matの型や内容を確認する方法##

超基本的な事ですがOpenCVのMatクラスの型や内容を確認する方法も確認しておきましょう。

```csharp:MatBasicProcessingExample.cs#L201-L211
            // 8U, channels=1, 3x3 //（byte型、1チャンネル、3x3のMatを作成する）
            Mat mat1 = new Mat (3, 3, CvType.CV_8UC1, new Scalar (1)); //（要素の初期値として1をセット）

            // 8U, channels=4, 3x3 //（byte型、4チャンネル、3x3のMatを作成する）
            Mat mat2 = new Mat (3, 3, CvType.CV_8UC4, new Scalar (1, 2, 3, 4)); //（要素の初期値として各チャンネルにそれぞれ1,2,3,4をセット）

            // dump
            Debug.Log ("mat1=" + mat1); //（Matのサイズ、型、データが連続して配置されているかどうか、部分行列かどうかを出力）
            Debug.Log ("mat1.dump()=" + mat1.dump ()); //（Matの内容を出力、三次元以上の配列にの出力には未対応）
            Debug.Log ("mat1=" + mat2);
            Debug.Log ("mat2.dump()=" + mat2.dump ());
```
コンソール出力結果

```
mat1=Mat [ 3*3*CV_8UC1, isCont=True, isSubmat=False, nativeObj=0x793787296, dataAddr=0x794435328 ]
mat1.dump()=[  1,   1,   1;
   1,   1,   1;
   1,   1,   1]
mat2=Mat [ 3*3*CV_8UC4, isCont=True, isSubmat=False, nativeObj=0x793789648, dataAddr=0x799875584 ]
mat2.dump()=[  1,   2,   3,   4,   1,   2,   3,   4,   1,   2,   3,   4;
   1,   2,   3,   4,   1,   2,   3,   4,   1,   2,   3,   4;
   1,   2,   3,   4,   1,   2,   3,   4,   1,   2,   3,   4]
```

そのほかのOpenCVのMatクラスの基本的なデータ構造や処理方法については、

- [MatBasicProcessingExample](https://github.com/EnoxSoftware/OpenCVForUnity/blob/master/Assets/OpenCVForUnity/Examples/Basic/MatBasicProcessingExample/MatBasicProcessingExample.cs)
- [cv::Matの基本処理](http://opencv.jp/cookbook/opencv_mat.html#id41)

を読めばだいたい学べると思います。

#まとめ#

**OpenCV for Unityはデフォルト状態ではエラーログがコンソールに出力されない。
エラーログの出力させるためにはUtils.setDebugメソッドでデバッグモードをONにする必要がある。**

使い方講座の次の記事は
[OpenCV for Unityの使い方講座２　Texture2DとMatの相互変換編](https://qiita.com/utibenkei/items/d1050253fc586f17f0ec)
です。