OpenCvSharpはもともとC言語のOpenCV 1.0 APIを対象に作っておりました。以来6年経ち、C++ APIが充実し主流になってきたため、そのサポートを進めています。
ある程度は仕様が固まってきたので、使い方をご紹介していこうと思います。今回は大まかな概観です。2014/3/28リリース版をベースに書いています。
導入
おすすめはNuGetで。http://schima.hatenablog.com/entry/2013/12/15/110513
※Linux, Mac等では自分でビルドが必要ですが、そこまで手が回っておらず、残念ながらまだ現状使えないと思われます。
C++ APIは、OpenCvSharp.CPlusPlus名前空間の中にまとめられています。enum定義流用、従来のIplImageとの共存などにより、実装としてはOpenCvSharp.dllに依存しています。以降は、コードの頭にこれが書いてある前提です。
using OpenCvSharp; using OpenCvSharp.CPlusPlus;
設計ポリシー
Mat
従来の IplImage, CvMat, CvMatND という画像(行列)を表す型は、OpenCV 2.0以降はMatという1つのクラスにまとめられました。OpenCvSharpでも、何をするにしろこれが基本になってきます。
サイズと型を指定するもの、既存の配列をデータにするもの、画像から読み込むもの(imread相当)といったコンストラクタがあります。
// 100行200列、double型 1チャネル で初期化。 Mat mat1 = new Mat(100, 200, MatType.CV_64FC1); // 画像から読み込み。 Mat mat2 = new Mat("hoge/fuga/piyo.png"); // マネージ配列から。データは共有される(どちらかを変えればもう片方も変わる)。 int[] array = {1, 2, 3, 4, 5}; Mat mat3 = new Mat(array.Length, 1, MatType.CV_32SC1, array);
Matの操作
C++にはかないませんが、C#も演算子オーバーロード、インデクサ定義などができますので、極力食らいついて似たような書き方ができるようにしています。
インデクサにより、部分行列や、指定した行・列の範囲取得ができます。代入もできます。
// 指定した範囲に、otherMatを3倍し転置した結果をセット mat[10, 20, 100, 110] = (otherMat * 3).T(); // 100~200行目を、300~400行目を反転した結果でセット mat.Row[100, 200] = ~(mat.Row[300, 400]); // 単位行列から、それぞれ5を引いた行列 Mat mat2 = Mat.Eye(10, 10, MatType.CV_64FC1) - 5;
関数
OpenCV2以降のcv名前空間にある関数、たとえば cv::cvtColor のような関数は、OpenCvSharpでは Cv2.CvtColor として利用することができます。(Cv2という名前はPythonのAPIを参考にしています。)
Cv2. まで入力し、インテリセンスで使えそうなメソッドを眺めてみてください。
C APIでは、出力データの領域はあらかじめ確保する必要がありました。C++ APIでは、多くの関数でその内部で自動的に出力データを確保してくれます。
// カラー画像(src) から グレースケール(dst) に変換 cv::Mat src = cv::imread("foo.jpg"); cv::Mat dst; cv::cvtColor(src, dst, CV_BGR2GRAY);
OpenCvSharpでもこれと同様です。出力先のMatは空で作っておくだけでよいです(自分で確保してもよいです)。
Mat src = Cv2.ImRead("foo.jpg"); Mat dst = new Mat(); Cv2.CvtColor(src, dst, ColorConversion.BgrToGray);
部分行列を対象にすると、従来のROIと同じような操作ができます。以下の例は、ある行列の別の部分に、ガウシアンフィルタの結果をセットしています。
Cv2.GaussianBlur( mat[100, 200, 100, 200], mat[200, 300, 200, 300], new Size(7, 7), 20);
InputArray / OutputArray
OpenCV 2.3以降では、関数定義には InputArray / OutputArray が使われるようになっています。これにより、Mat, vector<T>, vector<vector<T>>, Scalar, ... などなど、いろんなものを引数として投げ入れることができるというわけで、CvArrがすごくなって再来という感じですね。
OpenCvSharpでは、C++ほどの表現力はできませんが、いくらかサポートしています。
先ほどの例の CvtColorは、次のような定義になっています。先の例ではMatを渡していましたが、表面上はMatはどこにも出てきません。
class Cv2 { public static void CvtColor(InputArray src, OutputArray dst, ColorConversion code); }
Mat -> Input(Output)Array
Matを使っている限りは、基本的に気にする必要はありません。Input(Output)Array = Mat と思って使ってください。OpenCvSharp側で自動的に変換してネイティブ関数に渡します(暗黙のキャストがあります)。
IEnumerable<T> -> InputArray
C++でのvectorサポートを模して、OpenCvSharpのInputArrayには、IEnumerable<T>を渡すことができます。また、OutputArrayはList<T>として受け取ることができます。
以下は、C#の普通のデータ型で閾値処理をする例です。(C#のジェネリクスの制約上暗黙のキャストができず、InputArray.Create というような書き方を余儀なくされます。)
byte[] input = {1, 2, 3, 4, 5, }; List<byte> output = new List<byte>(); // outputには、3以下が0, 4以上は5としてセットされる Cv2.Threshold(InputArray.Create(input), OutputArray.Create(output), 3, 5, ThresholdType.Binary);
Tとしてはbyte, float等のプリミティブ型や、Vec3b, Point2f のようなOpenCVの値型を指定することができます。
ちなみにMatで同じことを書くと、このようになります。MatOfByteはC++でいうcv::Mat_<uchar>相当の、型指定のMatです。これを使うとマネージ配列に戻しやすいです。
充分こちらも簡単に使えるので、お好きなほうをどうぞ。
byte[] input = {1, 2, 3, 4, 5, }; MatOfByte src = new MatOfByte(input.Length, 1, input); MatOfByte dst = new MatOfByte(); Cv2.Threshold(src, dst, 3, 5, ThresholdType.Binary); byte[] output = dst.ToArray();
おわりに
core, imgprocの大部分は実装が終わり、基本的なところは支障なく使えるレベルと考えています。その他も進んできていますが、まだ全体でみると、半分に満たないくらいかと思います。
皆様の温かいご支援ご協力、つまり要するに pull request をお待ちしております!
https://github.com/shimat/opencvsharp