OpenCvSharpをつかう その21(C++ API 概要)

OpenCvSharpはもともとC言語OpenCV 1.0 APIを対象に作っておりました。以来6年経ち、C++ APIが充実し主流になってきたため、そのサポートを進めています。

ある程度は仕様が固まってきたので、使い方をご紹介していこうと思います。今回は大まかな概観です。2014/3/28リリース版をベースに書いています。

OpenCvSharpをつかう 記事一覧

導入

おすすめは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;

設計ポリシー

  • 従来通り、極力C++と似たような書き方で済むようにする。なんとなく使い始められること、C++サンプルコードから類推できることを重視。
  • ただしC++の表現力にはかなわないので、Java, PythonAPIも参考に、ある程度C#に即した感じにしたい。(従来はC APIの直訳が過ぎた箇所もあったかなと思います。)

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という名前はPythonAPIを参考にしています。)

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で同じことを書くと、このようになります。MatOfByteC++でいう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();

(MatOfByteというような名前は、Java APIを参考にしています。)

おわりに

core, imgprocの大部分は実装が終わり、基本的なところは支障なく使えるレベルと考えています。その他も進んできていますが、まだ全体でみると、半分に満たないくらいかと思います。

皆様の温かいご支援ご協力、つまり要するに pull request をお待ちしております!
https://github.com/shimat/opencvsharp