cvRedirectErrorはOpenCV2.0では使えない?

先日実装したOpenCvSharpのエラーハンドリング機能ですが、早くも暗雲が立ち込めております。


OpenCV2.0 beta現在での、cvRedirectError関数の実装はこうなっています(src/cxcore/cxsystem.cpp 248行目)。

CV_IMPL CvErrorCallback
cvRedirectError( CvErrorCallback, void*, void** )
{
    return 0;
}

・・・ひどい絶望感です。


OpenCVのrevision1610あたりから、エラーは全てC++のthrowでcv::Exceptionを投げるという仕様に変わり、代償として独自のリダイレクト関数は設定できなくなりました。よって、現在のOpenCV2.0 betaをC#からPInvokeで呼び出すと、例外が起こったという事実だけはSEHExceptionで知ることができますが、例外の中身はわかりません。


最悪、従来のようなGUIのメッセージボックスでのエラー表示でも良いのですが、その部分も2.0では削除されてしまっています。さすがにこれはまずいです。


一応バグとしては報告されているようで、パッチも作られています。この差分を適用して自分でOpenCVをコンパイルすれば、1.1pre以前と同様にcvRedirectErrorが使えるようになります。

http://sourceforge.net/tracker/?func=detail&aid=2737570&group_id=22870&atid=376677

しかし未だFixedとはなっておらずNoneのまま数ヶ月間放置されているようです。一縷の望みに賭けてbetaが取れるまでは様子見しようと思いますが、修正される望みは薄そうな感じがします。



もしこのまま正式版が出てしまったらOpenCvSharpはどうするか、選択肢を挙げてみます。

C++/CLIでラップする

C++/CLIなら何があろうと敵無しです。おそらく。

しかし、当初C++/CLIで実装していたのをわざわざC#に切り替えた手前、また戻すのは厳しい選択です。また、monoへの対応が絶望的になります。

パッチを適用し再コンパイルしたOpenCVのDLLを添付する

Emgu CVはこの方法でいくようです。

http://www.emgu.com/wiki/index.php/Version_History#Emgu.CV-2.0.0.0_Alpha

または自分でコンパイルしろ、とのこと。

OpenCV svn 1611 introduce a bug that no longer accept custom error handler. The bug and available patch has been reported to OpenCV's bug list, but so far has not been applied to the trunk. In order to enable exception handling, you will need to manually patch OpenCV's source code.

http://www.emgu.com/wiki/index.php/Download_And_Installation

ラッパーの開発者サイドとしては最もコストをかけずに対応することができます。しかし利用者サイドとしては結構な負担を強いられます。また、Windows以外ではどうするのかという問題はあります。そもそも、"wrapper"がwrapする中身に手を加えていいのか、ということがどうしても引っかかります。

Cのラッパーをあいだに挟む

C++/CLIではなくC++のネイティブのラッパーをあいだに挟みます。C#からDllImportで呼び出すのはOpenCVではなくこのラッパーに変更します。現在でもMLとcvblobのラップにはこの方法を使っています。多分こんな感じになるかと。

// cvRedirectErrorの独自実装。あらかじめこれでエラーハンドラを設定しておく。
static CvErrorCallback myCallback = NULL;
CVAPI(CvErrorCallback) cvRedirectError_(CvErrorCallback newFunc)
{
    CvErrorCallback oldFunc = myCallback;
    myCallback = newFunc;
    return oldFunc;
}

// 以下、OpenCVの関数を1つ1つ全部try-catchで囲う関数を書く・・・
CVAPI(CvMat*) cvCreateMat_( int rows, int cols, int type )
{
    try
    {
        return cvCreateMat(rows, cols, type);
    }
    catch(cv::Exception e)
    {
        if(myCallback)
        {
            // 例外からエラーハンドラにデータを移し替えて、呼び出す
            myCallback(e.code, e.func.c_str(), e.err.c_str(), e.file.c_str(), e.line, 0);
        }
    }
}

マクロを使えばぐっと短く書けるはずですが、やはりとてつもない労力がかかりそうです。それでも今のところ最善の策かと考えています。

あきらめる

ただの言い訳ですが、実際卒研の時以来このOpenCvSharpに多大なリソースを食われて研究が進まないのは事実。1.1でも充分だし、そろそろ手を引くのもありか?


C++実践プログラミング

C++実践プログラミング