どうも、○NAKAです。前回の続きです。
さて今回はOpenCvSharpを使って画像の読み取りをします。前回の記事をまだ読んでいない方は、こちらからアクセスできます。
読み取りする画像はこちらです。事前準備として、この画像をデスクトップ上にダウンロードしておきます。
画像に写っているのは河村友歌さんです。この顔に見覚えのある方は多いのではないしょうか。
ぱくたそという無料で写真を配布しているサイトで「フリー素材モデル」として活躍されている方で、あらゆるネット広告のアイキャッチ画像に採用されています。ぱくたそでダウンロードしたフリー素材だからブログに載せても問題はありませんよね。
ぱくたそはこちらからアクセスできます。
画像読み取りサンプル
OpenCvSharpの作成者のschimaさんが紹介している画像読み取りサンプルを使ってみましょう。
http://schima.hatenablog.com/entry/20090617/1245200179
上記のブログにアクセスして、記事を下にスクロールすると最後らへんにサンプルが載っています。
// 画像ファイルを読み込む
using (IplImage img = new IplImage("lenna.png"))
{
// "window"という名前のウィンドウをつくり、そこに画像imgを表示
using (new CvWindow("window", img))
{
// キー入力を待つ
Cv.WaitKey();
}
}
上記のプログラムでさっそくビルド開始しようとしたら、ビルドエラーが発生しました。エラー内容は「型または名前空間の名前’IplImage’が見つかりませんでした」という内容でした。ちょっとschimaさんどういうこと?と思いつつ、schimaさんのブログを確認してみると、OpenCvSharp3以降は以下の変更点が発生しているとのことです。
- IplImage型がなくなった
- Cvクラス、CvWindowクラスなどがなくなった
代わりにCv2クラスやWindowクラスを使うことになったようです。IplImageは使用できなくなりましたが、Matを使うことで画像の読み取りができます。
http://schima.hatenablog.com/entry/2015/08/17/203901
OpenCvSharp3以降のバージョンで画像の読み取りをするコードは以下になります。
public partial class MainWindow : System.Windows.Window
{
public MainWindow()
{
InitializeComponent();
Loaded += (s, e) =>
{
//画像のファイルパスを取得
var filepath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "freepicture.jpg");
// 画像ファイルを読み込む
var img = new Mat(filepath);
//画像の表示
using (new OpenCvSharp.Window("image", img))
{
Cv2.WaitKey();
}
};
}
}
これを実行してみると、ちゃんと画像を読み取りしてWindowに表示することができました。
WPFで画像の読み取り
別Windowを開いて、画像の読み取りをすることができました。次はWPFのImageコントロールに読み込んだ画像を表示します。
UI画面の実装
マテリアルデザインを適用させたWPFのプロジェクトを使います。このプロジェクトの詳細については前の記事を参照してください。
UserControlOpenCvSharpという名前でユーザーコントロールを追加します。デザイナーを開いて、Imageコントロールと画像を追加するためのボタンを配置します。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel
Grid.ColumnSpan="2"
HorizontalAlignment="Left"
Orientation="Horizontal">
<materialDesign:PackIcon
Width="35"
Height="35"
Margin="10"
VerticalAlignment="Center"
Foreground="{StaticResource PrimaryHueMidBrush}"
Kind="PictureInPictureTopRightOutline" />
<TextBlock
Margin="0,0,40,0"
HorizontalAlignment="Center"
Style="{StaticResource MaterialDesignHeadline4TextBlock}"
Text="OpenCvSharp" />
</StackPanel>
<StackPanel
Grid.ColumnSpan="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
x:Name="btnGetImage"
Width="100"
Margin="10,0"
Click="btnGetImage_Click"
Content="画像取得" />
<Button
x:Name="btnProcessImage"
Width="100"
Margin="10,0"
Click="btnProcessImage_Click"
Content="画像変換" />
<Button
x:Name="btnSaveImage"
Width="100"
Margin="10,0"
Click="btnSaveImage_Click"
Content="画像保存" />
</StackPanel>
<Image
x:Name="ImageData"
Grid.Row="1"
Grid.ColumnSpan="2"
Margin="5,5,5,5"/>
</Grid>
コード実装
WPFに取得した画像を表示するのは、上で書いたサンプルを少しいじるだけです。ImageコントロールのImageSourceプロパティにMat形式の画像データは渡せないので、ToWriteableBitmapメソッドを使ってWriteableBitmap形式に変換します。
private void btnGetImage_Click(object sender, RoutedEventArgs e)
{
//画像のファイルパスを取得
var filepath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "freepicture.jpg");
// 画像ファイルを読み込む
var img = new Mat(filepath);
//画像を表示する
ImageData.Source = img.ToWriteableBitmap();
}
画像をグレースケールに加工
次に取得した画像をグレースケールに加工をします。グレースケールに加工するにはCvtColorメソッドを使います。
第1引数には、加工したいMat形式のデータを指定します。
第2引数には第1引数の画像を加工したMat形式のデータを設定する変数を設定します。
第3引数には、加工するタイプを設定します。加工するタイプは、ColorConversionCodesの列挙体に定義されているものから選択します。グレースケールにする場合、RGB2GRAYを選択します。他にも100種類以上定義されていますので、色々と試してみるといいでしょう。
private void btnProcessImage_Click(object sender, RoutedEventArgs e)
{
if (ImageData.Source == null) return;
//画像データをBitmapSource形式で取得し、Mat形式に変換
var mat = OpenCvSharp.WpfExtensions.BitmapSourceConverter.ToMat((BitmapSource)ImageData.Source);
//グレースケールに加工
OpenCvSharp.Cv2.CvtColor(mat, mat, OpenCvSharp.ColorConversionCodes.RGB2GRAY);
//加工した画像を表示する
ImageData.Source = mat.ToWriteableBitmap();
}
加工した画像を保存
加工した画像を保存する処理を追加します。保存する時のファイル名が被らない様に末尾に時刻を入力し、「Image-”保存した時刻”.png」というファイルとします。
FilrStreamクラスを作成し、PngBitmapEncoderのフレームに保存したい画像を追加して保存します。画像ファイルはアプリケーション直下のフォルダに作成されます。
private void btnSaveImage_Click(object sender, RoutedEventArgs e)
{
if (ImageData.Source == null) return;
//ファイル名を指定
var filename = $"Image-{DateTime.Now.ToString("yyyyMMddHHmmss")}.png";
//出力用のFilrStreamを作成
using (Stream stream = new FileStream(filename, FileMode.Create))
{
//PngBitmapEncoderのフレームに保存したい画像を追加して保存
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create((BitmapSource)ImageData.Source));
encoder.Save(stream);
}
}
アプリケーションの起動
さっそく実装したプログラムを起動してみます。画像の読み取り・表示・加工をすることができています。短いコードでやりたいことができるなんてほんと凄いです。
まとめ
今回はサンプルコードを元に読み取りした画像をWPFに表示する方法とグレースケールに加工する方法について紹介しました。
次回もOpenCVSharpについて記事にしたいと思います。
以上、最後まで読んでいただきありがとうございました。