こんにちは、ゆたんぽです。
前回に引き続き画像処理のアプリ作成を進めていこうと思います。
今回は、Modelの実装を進めて、画像を選択し表示するところを進めていこうと思います。
実装
それでは、実際に実装を始めます。
Modelフォルダの作成
まず、下のようにModelを格納するフォルダを作成します。
上記のように「OpenCV_Prism」を右クリックして、「追加」→「新しいフォルダー」で新しいフォルダーを作成します。フォルダ名は上記のように「Models」としてください。
上記と同様に「Abstract」フォルダも作成します。
このフォルダは、「抽象クラス」を格納するフォルダです。今回実装していく、画像処理クラスの基本となるクラスを格納するフォルダです。わからない方は、設計図のようなものだと思ってください。
作成後のフォルダ構成
抽象クラスを作成していきます。
Abstructフォルダを右クリックして、「追加」→「クラス」を選択した、クラスの作成を行ってください。
クラスの名前は、「ImageProcess.cs」にします。
プログラム作成前にFodyをインストールします。
以前の記事を参考にしてください。
以下に実装プログラムを載せます。
using OpenCvSharp;
using PropertyChanged;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Windows.Forms;
namespace OpenCV_Prism.Abstract
{
[AddINotifyPropertyChangedInterface]//①
public abstract class ImageProcess : INotifyPropertyChanged//②
{
#region 【INotifyPropertyChanged】//③
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
#region 【メンバー】
protected BitmapImage _originImage;//★
#endregion
#region 【プロパティ】
public BitmapImage Image { get; set; }//④
#endregion
#region 【コンストラクタ】
public ImageProcess()
{
_originImage = new BitmapImage();//★
Image = new BitmapImage();//⑤
}
#endregion
#region 【メソッド】
//画像選択メソッド
public void ImageSelect()//⑥
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "画像|*.jpg;*.jpeg;*.png;*.bmp";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
var imagePath = openFileDialog.FileName;
Image = new BitmapImage(new Uri(imagePath));
}
}
//画像処理
public abstract void myRun();//⑦
#endregion
}
}
上記のようにImageProcess.csのクラスを実装します。
今回作成したクラスは抽象クラスですのでAbstructをつけてください。
①~③は以前の記事で説明しているので割愛します。
④プロパティを宣言しています。ここでは、画面に表示する用の画像をBitmapImage型で指定しています。
⑤コンストラクタで、④で作成したImageをインスタンス化しています。
★一部追加します(2021.10.28)
★で示したBitmapImage型の変数を追加しました。これは、画像処理を行った際に選択した元の画像を格納しておくものです。
画像を選択する方法はOpenFileDialogを使用しています。
openFileDialog.Filterでは、選択するファイルの拡張を指定することができます。今回は画像を選択するので「*.jpg;*.jpeg;*.png;*.bmp」を指定しています。
If文内のopenFileDialog.ShowDialog() で、実際にダイアログを開いています。
画像を選択時などに、押されたボタンの種類が返ってきますので、IF文ではOKボタンが押された時の処理を記載しています。
var imagePath = openFileDialog.FileNameで、ダイアログで開いた画像のパスを取得しています。
Image = new BitmapImage(new Uri(imagePath))では、先ほど取得したパスからBitmapImageへ変換して格納しています。
実装クラスの作成
先ほど作成した抽象クラスを継承して実際に動かすプログラムを作成します。
ModelsフォルダにImageProcessExeという名前でクラスを作成してください。
using OpenCV_Prism.Abstract;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace OpenCV_Prism.Models
{
class ImageProcessExe : ImageProcess//①
{
public override void myRun()//②
{
MessageBox.Show("ImageProcessExe");
}
}
}
①ImageProcessの継承を行っています。継承を行うと、基本クラスの宣言した変数やメソッドが引き継がれます。
②継承したImageProcessで作成したabstructのメソッドをオーバーライドしています。
今回は、テストで「ImageProcessExe」をメッセージボックスで表示しています。
Viewで作成したボタンとModelの紐づけ
実際にModelを実装できましたので、今度はボタンを押したら実装したクラスが起動するようにしましょう。
ImageViewAreaViewModelに実装していきます。
using OpenCV_Prism.Models;
using Prism.Commands;
using Prism.Mvvm;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Windows.Media.Imaging;
namespace OpenCV_Prism.ViewModels
{
public class ImageViewAreaViewModel : BindableBase
{
// リアクティブプロパティ破棄
protected CompositeDisposable _disposables = new CompositeDisposable();//①
ImageProcessExe Ipe;//②
public ReactiveProperty<BitmapImage> Image { get; set; }//③
public DelegateCommand ImageSelect { get; set; }//④
public DelegateCommand ImageProcess{ get; set; }
public ImageViewAreaViewModel()
{
Ipe = new ImageProcessExe();//⑤
ImageSelect = new DelegateCommand(ImageSelectExe);//⑥
ImageProcess = new DelegateCommand(ImageProcesExe);
Image = Ipe.ToReactivePropertyAsSynchronized(x => x.Image)//⑦
.AddTo(_disposables);
}
private void ImageSelectExe()//⑧
{
Ipe.ImageSelect();
}
private void ImageProcesExe()
{
Ipe.myRun();
}
}
}
上記に示したプログラムを説明していきます。
①disposeメソッドを定義しています。今回、ImageをView⇔ViewModel⇔Model間で連携するためにReactivePropertyを使用するのですが、その際に使用します。
②先ほど定義したImageProcessExeを使用するために定義しています。
③ReactivePropertyでBitmapImage型の変数を定義しています。ここに選択された画像が入ります。
④Viewのボタンでデータバインディングした名前と同じ名前のコマンドを定義しています。
これによりView⇔ViewModel間のデータが紐づきます。
⑤コンストラクタで ImageProcessExeをインスタンス化しています。
⑥ ④で定義したDelegateCommandの変数に実際に動くメソッドを入れています。
メソッドは後ほど説明します。
⑦ ③でReactivePropertyを使用して定義したBitmapImage型のImageをModel側と紐づけています。コンストラクタでインスタンス化した ImageProcessExe内にあるImageとToReactivePropertyAsSynchronizedを使用して関連付けています。最後にAddToを入れてDispose処理がされるように定義しましょう。
⑧ここでは、ボタンと紐づけたDelegateCommandの処理メソッドを記載しています。それぞれ ImageProcessExe 内にある、ImageSelectとmyRunのメソッドを処理する記述になっています。
上記を実装することで、ボタンを押された際にModel側で定義したメソッドを呼び出して起動することができるようになります。また、ImageはView⇔ViewModel⇔Modelで関連付けを行ったので、Modelで処理されたBitmapImageがView側に表示されるはずです。
ここまで来たらデバッグを開始してみてください。
上記のようにSelectボタンを押して選択した画像が表示されて、Exeボタンではメッセージボックスが表示されれば成功です。
まとめ&次回予告
今回は、画面選択してアプリの画面に画像を表示するところまで説明しました。
画像を選択できるようになったのでいよいよ次回からOpenCVを使って画像処理を行っていきます。
遅くなって申し訳ありませんが次回もよろしくお願いします。
4.OpenCVで画像処理アプリを作ろう(画像グレー化)