Visual Basic でOpenCV④ - 色の処理

Visual BasicOpenCVを使用し色や輝度の処理を行います。標準的なWindows フォームを使用し色や輝度の処理を行います。

反転

画像の色を反転するプログラムを紹介します。

プログラム開発

画像処理の説明に先立ち、フォームを簡単に説明します。MenuStripコントロール、StatusStripコントロール、OpenFileDailogコントロール、Panelコントロール、そしてPictureBoxコントロールなどを配置します。PanelコントロールとPictureBoxコントロールは依存関係があります。

フォームにコントロールを貼り付ける順序が異なると、Panel コントロールがMenuStripコントロールやStatusStripコントロールの下に入り込むときがあります。これを避けるため、最初にMenuStripコントロールやStatusStripコントロールを配置し、次にPanel コントロールを配置します。最後のPictureBox コントロールはPanel コントロールの上に配置します。

メニューを以降に示します。メニューに対応するメソッドは、メニュー項目をダブルクリックすると、自動的にメソッドが定義され、該当のソースファイル部分へカーソルが移動します。その部分に、適宜コードを記述します。

以降に、ソースリストを示します(Form1.vb)。

Imports System
Imports System.IO
Imports OpenCvSharp

Public Class Form1

    Private ttl As String = "sample"
    Private mSrc As Mat

    Public Sub New()
        MyBase.New
        InitializeComponent()

        Text = Me.ttl
        OpenFileDialog1.FileName = ""
        ToolStripStatusLabel1.Text = "Status"
        Panel1.Dock = DockStyle.Fill        'スクロール対応
        Panel1.AutoScroll = True
        PictureBox1.Location = New Drawing.Point(0, 0)
    End Sub

    ' 開く」メニュー項目
    Private Sub FileMenuOpen_Click(sender As Object, e As EventArgs) _
                                                            Handles FileMenuOpen.Click
        Try
            OpenFileDialog1.CheckFileExists = True
            OpenFileDialog1.Filter = ("画像ファイル(*.bmp,*.jpg)|*.bmp;*.jpg|" _
                                            + "すべてのファイル(*.*)|*.*")
            OpenFileDialog1.FilterIndex = 1
            If (OpenFileDialog1.ShowDialog = DialogResult.OK) Then
                Me.mSrc = Cv2.ImRead(OpenFileDialog1.FileName)
                PictureBox1.Image = Extensions.BitmapConverter.ToBitmap(Me.mSrc)
                PictureBox1.Size = PictureBox1.Image.Size

                ' ウィンドウサイズ調整
                ClientSize = New Drawing.Size(PictureBox1.Width, PictureBox1.Height _
                                    + MenuStrip1.Height + StatusStrip1.Height)

                'ファイル名表示
                ToolStripStatusLabel1.Text = Path.GetFileName(OpenFileDialog1.FileName)
            End If

        Catch ex As Exception
            MessageBox.Show(ex.Message)
        End Try

    End Sub

    ' 「処理」メニュー項目
    Private Sub ToolMenuEffect_Click(sender As Object, e As EventArgs) _
                                                            Handles ToolMenuEffect.Click
        Try
            If PictureBox1.Image Is Nothing Then Return          ' 読み込んでいるか

            Cursor = Cursors.WaitCursor

            Using dst = New Mat
                Cv2.BitwiseNot(Me.mSrc, dst)                            ' negative
                PictureBox1.Image = Extensions.BitmapConverter.ToBitmap(dst)
            End Using
        Catch ex As Exception
            MessageBox.Show(ex.Message)
        Finally
            Me.Cursor = Cursors.Default
        End Try

    End Sub

    ' 「閉じる」メニュー項目
    Private Sub FileMenuClose_Click(sender As Object, e As EventArgs) _
                                                            Handles FileMenuClose.Click
        Close()
    End Sub

End Class

コンストラクターでコントロールの各種設定を行います。たとえば、Panel コントロールのAutoScroll プロパティをTrue に設定します。PictureBox コントロールはPanel コントロール上に配置されています。Panel コントロールのAutoScroll プロパティをTrue に設定しているので、PictureBox コントロールのサイズが Panel コントロールより大きくなると自動でスクロールバーが現れます。
[開く]メニュー項目が選択されるとFileMenuOpen_Click メソッドが呼び出されます。例外を捕捉するためにTry と Catch で囲みます。まず、OpenFileDialog オブジェクトの ShowDialog メソッドで「ファイルを開く」ダイアログを表示させます。使用者がファイルを選択し[開く]を選択すると、ShowDialog メソッドはDialogResult.OK を返します。使用者が[キャンセル]を選択すると、DialogResult.OK 以外が返ってきます。キャンセルされた場合はすぐにメソッドを抜けます。ファイルが選択されたら、OpenCvSharp のImRead メソッドでファイルを読み込みます。それをMat オブジェクトmSrc へ設定します。このままではVB.NET のPictureBoxコントロールへ表示できないため、OpenCvSharp.Extensions.BitmapConverter.ToBitmap メソッドでBitmap オブジェクトへ変換します。これをPictureBox オブジェクトのImage プロパティに設定することで、画像をフォームに表示します。
次に、PictureBox コントロールのSize プロパティを画像サイズに合わせます。さらに、フォームの ClientSize プロパティを画像の大きさに合わせ、画像を表示できるサイズに変更します。幅は画像の幅をそのまま使用しますが、高さは画像の高さに加え、MenuStripコントロールやStatusStripコントロールの高さを加えます。
最後に、表示したファイル名をStatusStripコントロールに表示します。「ファイルを開く」ダイアログが返すFileName をそのまま表示したのでは、パスまで表示されてしまいます。そこで、Path クラスのGetFileName メソッドでパス名からファイル名のみを取り出し表示します。
[処理]メニュー項目が選択されると ToolMenuEffect_Click メソッドが呼び出されます。画像が読み込まれていなかったら、メソッドをすぐに抜けます。読み込まれていたらMat オブジェクトmSrc の色反転処理を行います。その結果を、[開く]メニュー項目の処理と同様にBitmap オブジェクトへ変換し、PictureBox コントロールのImage プロパティに設定します。
[閉じる]ボタンをクリックするとFileMenuClose_Click メソッドへ制御が渡ってきます。単純に、Close メソッドを呼び出し、プログラムを終了させます。

実行

以降に実行例を示します。まず、画像を読み込み、[処理]メニュー項目が選択したときの様子を図で示します。

画像ファイルを読み込むと、フォームに画像が表示されます。[処理]メニュー項目を選択すると、画像の色が反転されます。

グレイスケール

カラー画像をグレイスケール画像へ変換するプログラムを示します。ソースコードの変更は、ごく僅かです。ToolMenuEffect_ClickメソッドのBitwiseNotメソッド呼び出しをCvtColorメソッドへ変更するのみです。以降に、ソースコードの変更部分を抜粋して示します。先のプログラムの、
Cv2.BitwiseNot(Me.mSrc, dst)

Cv2.CvtColor(mSrc, dst, ColorConversionCodes.BGR2GRAY)
へ変更します。
以降に入力画像と処理結果を示します。実行前の画像はカラー、実行後の画像はグレイスケールです。

輝度平滑化

画像の輝度を平滑化するプログラムを紹介します。輝度が一部に偏っているとき、その部分を広げ、見やすくします。以降に、変更部分のコードを示します。

        :
        Using dst = New Mat
            Cv2.CvtColor(mSrc, dst, ColorConversionCodes.BGR2GRAY)  ' 輝度平滑化
            Cv2.EqualizeHist(dst, dst)                              ' 輝度平滑化
            PictureBox1.Image = Extensions.BitmapConverter.ToBitmap(dst)
        End Using
        :

本プログラムは、画像の輝度を平滑化します。OpenCvSharp の輝度平滑化関数は、グレイスケール画像を対象としているため、以前紹介した方法でカラー画像をグレイスケール画像へ変換します。Cv2.EqualizeHistメソッドで輝度平滑化後の画像をdst へ求めます。以降に、実行例を示します。

輝度が偏っていると、その部分が広げられるためコントラストが増したように見えます。輝度が極端に偏っている場合、その部分が引き伸ばされるため、暗い部分が、より暗くなる場合もあります。
なお、画像を読み込む際にCv2.ImReadメソッドの第2 引数にImreadModes.Grayscale を与えると、入力画像の種別にかかわらず、必ずグレイスケールで読み込むことができます。このような方法を採用すると、Cv2.CvtColorメソッドの呼び出しは省略できます。以降に、簡略化したコードを示します。

:
mSrc = Cv2.ImRead(OpenFileDialog1.FileName, ImreadModes.Grayscale)
:
Cv2.EqualizeHist(mSrc, dst)                           ' 輝度平滑化
:

閾値処理(スレッショルド処理)

画像に閾値処理(スレッショルド処理)へ行うには、Cv2.Thresholdメソッドを使用します。先ほどと同様、Cv2.Thresholdメソッドは、グレイスケール画像を対象としているため、カラー画像をグレイスケール画像へ変換します。閾値は60.0、最大値は210.0とします。そして閾値処理タイプはThresholdTypes.Binary を指定します。閾値処理(スレッショルド処理)後の画像をdst へ求めます。

WPF バージョン

本ブログでは基本的に「Windows フォームアプリ」を利用しますが、ここでは簡単に「WPF アプリケーション」も紹介します。既に、「WPF アプリケーション」の作成法は紹介していますので、ソースリストの説明を行います。以降に、MainWindow.xaml と MainWindow.xaml.cs のソースリストを示します。
まず、MainWindow.xamlのソースリストを示します。

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <DockPanel LastChildFill="True">
            <Menu DockPanel.Dock="Top" VerticalAlignment="Top">
                <MenuItem Header="ファイル(_F)">
                    <MenuItem Header="開く(_O)" Click="MenuItem_Open_Click"/>
                    <MenuItem Header="閉じる(_C)" Click="MenuItem_Close_Click"/>
                </MenuItem>
                <MenuItem Header="ツール(_T)">
                    <MenuItem Header="処理(_E)" Click="MenuItem_Effect_Click"/>
                </MenuItem>
            </Menu>
            <ScrollViewer HorizontalScrollBarVisibility="Auto"
                                            VerticalScrollBarVisibility="Auto">
                <Image Name ="Image" HorizontalAlignment="Left"
                                        VerticalAlignment="Top" Stretch="None"/>
            </ScrollViewer>
        </DockPanel>
    </Grid>
</Window>

ほとんど以前のものと同じです。変更点は、"ツール(T)"メニューが増えただけです。分かりやすいように、「ツール」メニューを示します。

次に、xaml に対するvb のソースリストを示します。

Imports Microsoft.Win32
Imports OpenCvSharp
Imports OpenCvSharp.WpfExtensions

Class MainWindow
    Private mMat As Mat

    Private Sub MenuItem_Open_Click(sender As Object, e As RoutedEventArgs)
        Dim dialog = New OpenFileDialog With {
                            .Filter = "全てのファイル (*.*)|*.*"
                    }

        If dialog.ShowDialog() = True Then
            mMat = New Mat(dialog.FileName)
            Image.Source = BitmapSourceConverter.ToBitmapSource(mMat)
            SizeToContent = SizeToContent.WidthAndHeight
        End If

    End Sub

    ' 「処理」メニュー項目
    Private Sub MenuItem_Effect_Click(sender As Object, e As RoutedEventArgs)
        If mMat Is Nothing Then Return

        Using dst = New Mat()
            Cv2.BitwiseNot(mMat, dst)                                   ' Negative
            Image.Source = BitmapSourceConverter.ToBitmapSource(dst)
        End Using
    End Sub

    ' 「閉じる」メニュー項目
    Private Sub MenuItem_Close_Click(sender As Object, e As RoutedEventArgs)
        Close()
    End Sub

End Class

画像を読み込むメソッドと加工するメソッドが別メソッドのため、画像を保存するMatオブジェクトをクラスのPrivateとし、メソッド間でアクセスできるようにします。次に「ツール」-「処理」メニュー項目に対応するMenuItem_Effect_Clickメソッドを追加します。処理内容は、Windows フォームアプリと、ほとんど同じです。本プログラムの、
Cv2.BitwiseNot(mSrc, dst)
を先のプログラムと同じように変更することで、グレイスケールなどへ変更できます。

実行

[開く]メニュー項目を選択して「開く」ダイアログが表示し、ファイルを選ぶと画像がウィンドウに表示されます。

[処理]メニュー項目を選択すると、画像の色が反転されます。また、ウィンドウサイズを画像より小さくするとスクロールバーが現れます。

先のプログラムの、
Cv2.BitwiseNot(mMat, dst)

Cv2.CvtColor(mMat, dst, ColorConversionCodes.BGR2GRAY)
Cv2.EqualizeHist(dst, dst)

へ変更し画像の輝度を平滑化したものを紹介します。輝度が一部に偏っているとき、その部分を広げ、見やすくします。

ここで紹介したプログラムはWindowsフォームアプリと同じような手法を採用しています。WPFアプリケーションですので、ImageのSourceなどへ画像をバインドするとプログラムは簡単になります。そのようなBindやViewModelを利用した例は後述する予定ですが、OpenCVの説明が趣旨ですので、あくまでも予定は未定です。