Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

Gtk# + OpenCVSharpでGUIアプリに画像を表示

前回OpenCVSharpをdotnetに導入するまでの手順をまとめました。
今回はdotnetで生成したGtkSharp + OpenCVSharpでWindowアプリケーション上に画像を表示させてみたいと思います!!

分かると簡単なのですが、結構ハマったところなので忘れないように備忘録残しておこうと思います。



■条件

今回の条件ですが下記になります。
・OS:Ubuntu20.04
dotnet:.NET 5.0

今回は、
 ・dotnetでGtkSharpが生成されていること
 ・OpenCVSharpがインストールされていること
を前提とします。
もし生成出来ていない場合にはこちらをご参考ください。
elsammit-beginnerblg.hatenablog.com

■GtkSharp + OpenCVSharpで画像を表示

では画像を表示させてみます。
まずWIndowアプリ上への画像表示用のImage Widgetを定義していきます。
MainWindow.gladeにGtkImage Widgetを追加します。
追加にはGladeというアプリを用いると簡単に行えます。
Gladeを用いてGtkImageを追加した結果はこちらのようになります。
f:id:Elsammit:20210424101219p:plain

今回GtkImage WidgetのIDをtestImgとしました。
命名センスがないことはご了承ください。。。

次にMainWindow.csを変更し表示する画像を定義していきます。
全体のコードはこちらになります。

private MainWindow(Builder builder) : base(builder.GetRawOwnedObject("MainWindow"))
{
    builder.Autoconnect(this);
 
    Mat mat = new Mat("画像ファイルパス");
    MemoryStream ms = new MemoryStream ();
    var bmp = BitmapConverter.ToBitmap(mat);
    bmp.Save(ms, ImageFormat.Png);
    ms.Position = 0;
    testImg.Pixbuf = new Gdk.Pixbuf(ms);

    DeleteEvent += Window_DeleteEvent;
    _button1.Clicked += Button1_Clicked;
}

今回OpenCVSharpで画像を表示するために追加したコードはこちら。

    Mat mat = new Mat("画像ファイルパス");
    MemoryStream ms = new MemoryStream();
    var bmp = BitmapConverter.ToBitmap(mat);
    bmp.Save(ms, ImageFormat.Png);
    ms.Position = 0;
    testImg.Pixbuf = new Gdk.Pixbuf(ms);

実際していることは、
Matデータをビットマップデータに変換し、ビットマップデータをメモリに書き込んでからImage Widgetに挿入・表示
です。
これだけ聞くとWindows FormやWPFとOpenCVSharpを組み合わせた画像表示と同じようにできそうですが、、、
こちらのコードの通り、ビットマップデータに変換後Image Widgetに挿入・表示するまでの処理に工夫が必要で簡単にはいかなかったです。

ビットマップデータをImage Widgetに挿入するだけであれば、

Mat mat = new Mat("画像ファイルパス");
var bmp = BitmapConverter.ToBitmap(mat);
testImg.Pixbuf = new Gdk.Pixbuf(bmp );

で問題ないように思えます。
しかしこちらのように直接挿入してしまうと

引数 1: は 'System.Drawing.Bitmap' から 'System.IntPtr' へ変換することはできません

といったエラーになってしまいうまく表示できません。

このためあえて、

MemoryStream ms = new MemoryStream();
var bmp = BitmapConverter.ToBitmap(mat);
bmp.Save(ms, ImageFormat.Png);

メモリを使用するためのストリームを定義(MemoryStream )し、
こちらにビットマップデータをフォーマットを指定して書き込む。
そして、メモリ上のデータをImage Widgetに挿入することで解決させることが出来ます。

ですが、、、
これだけでは不十分でした。。。
メモリ上にビットマップデータを保存したことにより、MemoryStreamで定義したメモリのアドレスがビットマップデータ分移動してしまうようです。
そこで、メモリ上のデータを挿入する前にMemoryStreamのアドレスを初期化する必要があり、

ms.Position = 0;

を追加。

細かいところが面倒。。。

■実際に動かしてみる

画像が表示されるだけではちょっと悲しいのでボタン押下すると画像が切り替わるコードにして動作させてみたいと思います!!
先ほどのコードからさらに、

private void Button1_Clicked(object sender, EventArgs a)
{
    Mat mat = new Mat("画像ファイルパス");
    MemoryStream ms = new MemoryStream ();
    var bmp = BitmapConverter.ToBitmap(mat);
    bmp.Save(ms, ImageFormat.Png);
    ms.Position = 0;
    testImg.Pixbuf = new Gdk.Pixbuf(ms);

    _counter++;
    _label1.Text = "Hello World! This button has been clicked " + _counter + " time(s).";
}

を追加します。
こちらのコードを追加することにより、最初に表示していた画像からボタン押下時に指定した画像に切り替わる制御になります。

では実行!!
結果はこちらのようになります。
f:id:Elsammit:20210424111035g:plain

画像が表示された状態でClick meを押下すると画像が切り替わったことがみて分かるかと思います。

■最後に

GtkSharpでOpenCVSharpを動かそうなんて人がほとんどいないためか、情報がほとんどなくとても苦労しました。
ですが、苦労した分出来た時はとてもうれしかったです!!
以前挫折したことが出来るようになってきたということは成長したということ??

今回は画像のみでしたが、動画表示もできたのでこちらも折を見てブログに載せておきたいと思います!!
ちょっとハマりポイントがありましたので忘れないうちにまとめておきます。