前回はカメラの説明を行いました。

Introduction

相当久しぶりですが… 今回は、OpenCVSharp 2.X から 3.1 に変更した話です。

OpenCV は2017年1月現在 2.4 と 3.1 が存在しています。
それに伴い OpenCVSharp も2つのバージョンが存在します。

ところがメジャーバージョンアップに伴い、大幅にAPIが変更されました。
このあたりの説明は OpenCVSharp の作者である Schima様の下記の記事を参考にしてください (作者様のページに全てが書いてあるからこの記事不要)。

1. IplImage の廃止

大きな変更点ですが、今までAPIに画像を渡すとき、入力画像の型が IplImage だったので、

1
2
var bitmap = new System.Drawing.Bitmap(100, 100);
var image = bitmap.ToIplImage();

とかしてましたが、これは消えました。
OpenCV 3.0からは Mat が推奨らしく、OpenCVSharp もそれにならい、こうなります。

1
2
var bitmap = new System.Drawing.Bitmap(100, 100);
var mat = bitmap.ToMat();

簡単ですね。
ただ、それに伴い、APIの型や名前空間が大幅に変わっているので、そのあたり合わせて変更します。
ハフ変換、モルフォロジー変換など変更は多岐にわたります。というか全部変わっています。

2. CvCapture の廃止

これが一番痛かったです。
CvCapture はコンストラクタにカメラインデックス(Int32)、動画又は画像ファイルへのフルパス(String)を入力できましたが、この型の代わりに新しく登場したのは、VideoCapture
そうこの型。画像ファイルが扱えません。

従来はこの型一つで画像と動画を区別することなく処理しましたがそれができません。また、フレームデータへのアクセスAPIが IplImage QueryFrame() から Mat RetrieveMat() に変わっています。

画像ファイルを読み込みたい場合は、Mat OpenCVSharp.Cv2.ImRead(string) でデータを取得できます。

ですが、面倒なのは、RetrieveMat の戻りは解放してはいけないが、ImRead の戻りは解放しなくてはならない点。
従来の CvCapture なら、戻りの IplImage は解放しないで、呼び出しのAPIを備えた CvCapture を破棄していましたが、それができません。
ここは注意が必要です。

利用した Mat の破棄を複数の箇所で実施しており、その先で読み込み元が画像なのかカメラまたは動画で区別するのが面倒なので、どうせ書き換えるなら、ということで私はラッパーを作りました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class MatWrapper : IDisposable
{

#region フィールド

private readonly bool _CanDispose;

private readonly Mat _Mat;

#endregion

#region コンストラクタ

public MatWrapper(Mat mat, bool canDispose)
{
this._Mat = mat;
this._CanDispose = canDispose;
}

#endregion

#region プロパティ

public Mat Mat
{
get
{
return this._Mat;
}
}

#endregion

#region IDisposable メンバ

/// <summary>
/// <see cref="System.IDisposable.Dispose"/> メソッドが呼ばれたかどうかを示す値を表します。
/// </summary>
private bool _Disposed;

/// <summary>
/// <see cref="MatWrapper"/> によって使用されているすべてのリソースを解放します。
/// </summary>
public void Dispose()
{
GC.SuppressFinalize(this);
this.Dispose(true);
}

/// <summary>
/// <see cref="MatWrapper"/> によって使用されているすべてのリソースを解放します。
/// </summary>
/// <param name="disposing">Dispose メソッドが呼ばれたかどうかを示す値。</param>
private void Dispose(bool disposing)
{
if (this._Disposed)
{
return;
}

this._Disposed = true;

if (disposing)
{
// マネージリソースの解放処理
if (this._CanDispose)
{
this._Mat?.Dispose();
}
}
}

#endregion

}

ImRead または RetrieveMat の戻りの Mat をコンストラクタに渡し、ソースが画像なら破棄可能として true を渡します。
これにより、このラッパークラスを破棄する際に、自動で内部の Mat を破棄するかどうかを判断します。

3. Native Dll が配置されない

OpenCVSharpをNugetから追加した際、NativeDllを含むパッケージをインストールし、ビルドしたにもかかわらずビルド出力先のディレクトリにNativeDllがコピーされず、動画ファイルの読み込みに失敗していました。
私はこれに数時間悩んでいました。

確認しましたが、OpenCVSharp 2.X でもビルドした際、関連するNativeDllが自動でコピーされていませんでした
なので手動でコピーする必要がありそうです。
Dllはソリューションフォルダと同じディレクトリから、packages\OpenCvSharp3-AnyCPU.3.1.0.20161105\NativeDlls\x64 という感じです。

というか、コピーしなくても動いていたのは、おそらくOpenCVをインストールして、パスが通っていたからでしょう。
なので、開発環境で動いて、本番環境で動かずに泣くことにならないようテスト環境の構築はきちんと行いましょう。

Conclusion

早めに OpenCVSharp3 への移行をお勧めします。ただし、一部APIが削除されていたりと、機能面で互換性が無くなっているところがあるので、そこは要注意です。
ですが、今後の新機能は3.0に追加されていくと思うので、その対応のためにも3.0への移行は必須です。
ただ、OpenCVSharpそのものが対応しないとダメですが….