> 作業効率UP!! オススメのモバイルモニターを紹介

【WPF】OpenCVSharpで画像の読み取り

当ページのリンクには広告が含まれています。
  • URLをコピーしました!

どうも、○NAKAです。前回の続きです。
さて今回はOpenCvSharpを使って画像の読み取りをします。前回の記事をまだ読んでいない方は、こちらからアクセスできます。

あわせて読みたい
【WPF】OpenCvSharpの使い方を紹介 最近のスマートフォンやデジタルカメラで写真を撮ると、顔を自動で認識してピントをあわせてくれます。また、顔のパーツと輪郭の位置関係から登録されている顔であれば...

読み取りする画像はこちらです。事前準備として、この画像をデスクトップ上にダウンロードしておきます。

画像に写っているのは河村友歌さんです。この顔に見覚えのある方は多いのではないしょうか。
ぱくたそという無料で写真を配布しているサイトで「フリー素材モデル」として活躍されている方で、あらゆるネット広告のアイキャッチ画像に採用されています。ぱくたそでダウンロードしたフリー素材だからブログに載せても問題はありませんよね。

ぱくたそはこちらからアクセスできます。

ぱくたそ(PAKUTASO)
フリー素材 ぱくたそ - すぐに使える無料の写真素材・AI画像素材 「ぱくたそ」は、会員登録せずに今すぐダウンロードできる無料の写真素材・AI画像素材のフリー素材サイトです。一部を有料販売したり、枚数制限による課金など一切ありませ...

画像読み取りサンプル

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のプロジェクトを使います。このプロジェクトの詳細については前の記事を参照してください。

あわせて読みたい
【WPF】Material Designでオシャレなメニュー画面を作成する方法−PART2 前回の続きです。 Material Design の「DrawerHost」というコントロールを使って、左横からスライドイン・スライドアウトするメインメニューを作成しました。 「Materia...

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について記事にしたいと思います。

以上、最後まで読んでいただきありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!