OpenCVのエラーコード，エラーメッセージ表示機能，例外処理機能


この記事は[OpenCV Advent Calendar 2015](http://qiita.com/advent-calendar/2015/opencv)の3日目の記事です．

# はじめに
OpenCVは，エラー内容に応じたエラーコードを定義しているだけでなく，エラーメッセージ表示機能，例外処理機能を提供しています．

この記事では，

* OpenCVで定義されるエラーコードの意味を理解することで**「開発者がエラーメッセージからソースコードのどの箇所でどのような問題が起きているかを把握できるようになる」**

だけでなく，

* OpenCVが提供するエラーメッセージ表示機能，例外処理機能の使い方を理解することで**「開発者が適切なエラーメッセージを表示したり，例外処理の実装を行えるようにする」**

ことを目的としています．

# エラーコード一覧表
以下の表にOpenCVが定義するエラーコードとその意味をまとめています．
ソースコード上では<code>core/include/opencv2/core/base.hpp</code>で定義されているため，興味のある方は読んでみるとよいでしょう．

| エラーコード | 意味 |
|:--|:--|
|StsOk0|正常|
|StsBackTrace|BackTraceのための疑似的なエラー|
|StsError|不明なエラー|
|StsInternal|内部エラー|
|StsNoMem|メモリ不足|
|StsBadArg|不正な引数/パラメータ|
|StsBadFunc|非サポートの関数/機能|
|StsNoConv|収束しない場合のエラー|
|StsAutoTrace|AutoTraceの呼び出し|
|HeaderIsNull|イメージヘッダーがNULL|
|BadImageSize|不正なイメージサイズ|
|BadOffset|不正なオフセット値|
|BadDataPtr|不正なDataPtr|
|BadStep|不正なステップ値|
|BadModelOrChSeq|不正なモデル/シーケンス|
|BadNumChannels|無効なチャンネル数|
|BadNumChannel1U|無効なチャンネル数<br>（1U:1チャンネル符号なし）|
|BadDepth|不正なdepth値|
|BadAlphaChannel|無効なアルファチャンネル|
|BadOrder|不正なOrder指定|
|BadOrigin|不正なOrigin指定|
|BadAlign|不正なアライメント|
|BadCallBack|不正なコールバック|
|BadTileSize|不正なタイルサイズ|
|BadCOI|不正なCOI(Channel of Interest)指定|
|BadROISize|不正なROI(Region of Interest)指定|
|MaskIsTiled|マスクがタイル配列|
|StsNullPtr|ヌルポインタが渡ってくるべきでない<br>箇所でヌルポインタが渡された|
|StsVecLengthErr|vector配列の長さが適切でない|
|StsFilterStructContentErr|不正なフィルター構造|
|StsKernelStructContentErr|不正なカーネル構造|
|StsFilterOffsetErr|不正なフィルターのオフセット値|
|StsBadSize|不正なサイズ|
|StsDivByZero|ゼロ除算の発生|
|StsInplaceNotSupported|in-place操作が非サポート|
|StsObjectNotFound|オブジェクトが見付からない|
|StsUnmatchedFormats|アンマッチなフォーマット指定|
|StsBadFlag|不正なフラグ指定|
|StsBadPoint|不正な座標値指定|
|StsBadMask|不正なマスク|
|StsUnmatchedSizes|アンマッチなサイズ指定|
|StsUnsupportedFormat|非サポートのフォーマット指定|
|StsOutOfRange|パラメータが範囲外の値|
|StsParseError|パースエラー|
|StsNotImplemented|未実装の関数/機能|
|StsBadMemBlock|メモリブロック破壊|
|StsAssert|アサーションエラー|
|GpuNotSupported|CUDA機能を無効にしたOpenCVライブラリで<br>CUDA機能を使用した場合のエラー|
|GpuApiCallError|CUDA API呼び出し時のエラー|
|OpenGlNotSupported|OpenGL機能を無効にしたOpenCVライブラリで<br>OpenGL機能を使用した場合のエラー|
|OpenGlApiCallError|OpenGL API呼び出し時のエラー|
|OpenCLApiCallError|OpenCL API呼び出し時のエラー|
|OpenCLDoubleNotSupported|OpenCLデバイスが倍精度浮動小数点数<br>（double型）を非サポート|
|OpenCLInitError|OpenCL初期化時のエラー|
|OpenCLNoAMDBlasFft|OpenCL AMD BLAS機能が利用できない|

# エラーメッセージ表示
## CV_Errorマクロを使う方法
OpenCVで提供される<code>CV\_Error</code>マクロを用いることでエラーメッセージ表示を容易に記述することができます．このマクロの第一引数はエラーコード，第二引数はエラーメッセージの文字列です。
詳細は[公式ドキュメント](http://docs.opencv.org/3.0.0/db/de0/group__core__utils.html#ga5b48c333c777666e076bd7052799f891)を参照ください．

以下に<code>CV\_Error</code>マクロの使用例を記述します．

<code>CV_Error(cv::Error::BadNumChannels, "Unsupported channels count");</code>

この例ではエラーコードとして<code>BadNumChannels</code>を指定しており，以下のようなエラーメッセージが表示されます．
<code>OpenCV Error: Bad number of channels (Unsupported channels count) in hoge, file fuga.cpp, line 12</code>

このエラーメッセージは以下のような意味を表しています．

| メッセージ | 意味 |
|:--|:--|
|<code>Bad number of channels</code>|エラーコードに対応した文字列．<br>この例ではBadNumChannelsに対応した文字列．|
|<code>(Unsupported channels count)</code>|<code>CV\_Error</code>マクロの第二引数に指定した文字列|
|<code>in hoge</code>|エラー発生関数．<br>この例ではhoge関数内でエラーが発生したことを表す．|
|<code>file fuga.cpp, line 12</code>|エラー発生したファイル名と行番号．<br>この例ではfuga.cppの12行目でエラーが発生したことを表す．|

また，<code>Bad number of channels</code>という文字列は<code>BadNumChannels</code>というエラーコードに対応した文字列です．他のエラーコードにも同様に各エラーコードに対応した文字列が用意されています．ソースコード上では<code>modules/core/src/system.cpp</code>のcvErrorStr関数で定義されているため，興味のある方は読んでみるとよいでしょう．

### CV\_Error\_マクロ
<code>CV\_Error</code>マクロではあらかじめ決まった書式でエラーメッセージの表示が行われます．
書式指定を用いたエラーメッセージを出力を行いたい場合は[CV\_Error\_](http://docs.opencv.org/3.0.0/db/de0/group__core__utils.html#ga1c0cd6e5bd9a5f915c6cab9c0632f969)マクロにより実現できます．

## CV_Assertマクロを使う方法
<code>CV_Assert</code>マクロを用いることで指定した条件式を満たさない場合にエラーメッセージを表示させることができます．詳細は[公式ドキュメント](http://docs.opencv.org/3.0.0/db/de0/group__core__utils.html#gaf62bcd90f70e275191ab95136d85906b)を参照ください．

以下に<code>CV_Assert</code>マクロの使用例を記述します．

```cpp
int i = -1;
CV_Assert(i >= 0);
```

この例では<code>CV_Assert</code>呼び出し時に<code>i >= 0</code>という条件を満たさないので
<code>CV_Assert</code>マクロ内で<code>cv::error</code>関数が呼ばれ，以下のようなエラーメッセージが表示されます．

<code>OpenCV Error: Assertion failed (i >= 0) in hoge, file fuga.cpp, line 8</code>

このエラーメッセージは以下のような意味を表しています．

| メッセージ | 意味 |
|:--|:--|
|<code>Assertion failed</code>|エラーコードに対応した文字列．<br>この例ではアサーションエラー発生（StsAssert）に対応した文字列．|
|<code>(i >= 0) </code>|エラーとなった理由．<br>この例では(i >= 0)という条件式を満たしていないため．|
|<code>in hoge</code>|エラー発生関数．<br>この例ではhoge関数内でエラーが発生したことを表す．|
|<code>file fuga.cpp, line 8</code>|エラー発生したファイル名と行番号．<br>この例ではfuga.cppの8行目でエラーが発生したことを表す．|

### CV_Assertマクロを使う方法の注意点
<code>CV\_Error</code>マクロを使ったエラーメッセージ表示では開発者がエラーコードを指定することができますが，<code>CV_Assert</code>マクロでは内部的にエラーコードが<code>cv::Error::StsAssert</code>となります．
（アサーションが発生するので当然と言えば当然ですが）

# 例外処理
OpenCVで提供される<code>cv::Exception</code>を用いることで例外処理を容易に記述することができます．
この関数の第一引数はエラーコード，第二引数はエラーメッセージの文字列，
第三引数は関数名，第四引数はファイル名，第五引数は行数です．詳細は[公式ドキュメント](http://docs.opencv.org/3.0.0/d1/dee/classcv_1_1Exception.html)を参照ください．

# サンプルコード
## サンプルコード（CV\_Errorマクロ）
<code>CV\_Error</code>マクロに対してエラー内容に応じたエラーコードを指定し，
エラーメッセージ表示を行うサンプルコードです．

```cpp:error_test.cpp
#include <opencv2/core.hpp> // coreモジュールにて定義
#include <iostream>

// 1チャンネルの画像しか受け付けない関数
void testFunc(cv::InputArray src_)
{
    cv::Mat src = src_.getMat();

    // 入力画像のチャンネル数が1以外の場合にエラーとする
    // ここではエラーコードとしてcv::Error::BadNumChannelsを指定
    if (src.channels() != 1)
        CV_Error(cv::Error::BadNumChannels, "Unsupported channels count");
}

int main(int argc, const char* argv[])
{
    // 3チャンネルの画像データを生成する
    cv::Mat img(cv::Size(512, 512), CV_8UC3, cv::Scalar(0, 0, 255));

    // testFunc関数を実行する
    testFunc(img);

    return 0;
}
```

このサンプルでは，1チャンネルの画像しか受け付けないtestFunc関数に対して
3チャンネルの画像を入力（＝**無効なチャンネル数を指定**）した例のため，
<code>CV\_Error</code>マクロの第一引数（エラーコード）に```cv::Error::BadNumChannels```を指定しています．

また，このサンプルを実行すると以下のようなエラーメッセージが表示されます．
このとき，エラーメッセージにはエラー内容だけでなくエラーが起きた関数名，ファイル名，行番号も表示されます．

<code>OpenCV Error: Bad number of channels (Unsupported channels count) in testFunc, file error_test.cpp, line 12</code>

## サンプルコード（cv::Exception）
<code>cv::Exception</code>にエラーコード，エラーメッセージ，関数名，ファイル名，行番号を指定し，
例外をキャッチした際にエラーメッセージ表示を行うサンプルコードです．

```cpp:exception_test.cpp
#include <opencv2/core.hpp> // coreモジュールにて定義
#include <iostream>

// 常にExceptionを投げる関数
void testFunc2(void)
{
    // エラーコード，エラーメッセージ，関数名，
    // ファイル名，行番号を指定してExceptionを投げる
    throw cv::Exception(cv::Error::StsError,
        "Unspecified error", CV_Func, __FILE__, __LINE__);
}

int main(int argc, const char* argv[])
{
    try
    {
        // testFunc2関数を実行する
        testFunc2();
    }
    catch (cv::Exception& e)
    {
        // 例外をキャッチしたらエラーメッセージを表示
        std::cerr << e.what() << std::endl;
        return -1;
    }

    return 0;
}
```

このサンプルを実行すると以下のようなエラーメッセージが表示されます．
このとき，エラーメッセージにはcv::Exceptionに指定したエラーメッセージ，
関数名，ファイル名，行番号が表示されます．

<code>exception_test.cpp:10: error: (-2) Unspecified error in function testFunc2</code>

# おわりに
* OpenCVが定義するエラーコード，エラーメッセージの意味を理解してデバッグに活用しましょう
* OpenCVが提供する<code>CV\_Error</code>マクロ，<code>cv::Exception</code>を活用して適切なエラー処理，例外処理を実装しましょう

# 備考
筆者は以下の環境で動作確認しました．

* [OpenCV 3.0.0](https://github.com/Itseez/opencv/releases/tag/3.0.0)
* Windows 8.1 Pro（64bit）
* Visual Studio 2013 Update5