NonSoft

Bitmap処理を高速化するサンプル(VB.NET)


 サンプル(ソース/コード)
<このサンプルの概要>
.NETのBitmap処理は遅いので高速化するサンプル(VB.NET)を作ってみました。
この方法で通常のBitmapで32秒掛かった処理が4秒で出来るようになりました。

このサンプルで使用しているBitmap高速化の方法は、
System.Runtime.InteropServices.Marshalを使用する方法です。

C#.NETでは、アンセーフコード(unsafe)を使用する方法も可能ですが、
VB.NETでは使用出来ないようです。

このサンプルのポイントとなるキーワードは以下の通りです。
(1)System.Drawing.Imaging.BitmapData.Scan0
(2)System.Drawing.Imaging.BitmapData.Stride
(3)GetPixel
(4)SetPixel

C#.NETのサンプルはこちらです。
Bitmap処理を高速化するサンプル(C#.NET)

★Bitmap処理を高速化するためのクラス(BitmapPlus.vb)
※System.Runtime.InteropServices.Marshalを使用する方法
Imports System.Drawing
imports System.Drawing.Imaging

Public Class BitmapPlus
    ''' <summary>
    ''' オリジナルのBitmapオブジェクト
    ''' </summary>
    Private _bmp As Bitmap = Nothing

    ''' <summary>
    ''' Bitmapに直接アクセスするためのオブジェクト
    ''' </summary>
    Private _img As BitmapData = Nothing

    ''' <summary>
    ''' コンストラクタ
    ''' </summary>
    ''' <param name="original"></param>
    Public Sub New(ByVal original As Bitmap)
        ' オリジナルのBitmapオブジェクトを保存
        _bmp = original
    End Sub

    ''' <summary>
    ''' Bitmap処理の高速化開始
    ''' </summary>
    Public Sub BeginAccess()
        ' Bitmapに直接アクセスするためのオブジェクト取得(LockBits)
        _img = _bmp.LockBits(New Rectangle(0, 0, _bmp.Width, _bmp.Height), _
            System.Drawing.Imaging.ImageLockMode.ReadWrite, _
            System.Drawing.Imaging.PixelFormat.Format24bppRgb)
    End Sub

    ''' <summary>
    ''' Bitmap処理の高速化終了
    ''' </summary>
    Public Sub EndAccess()
        If _img Is Nothing = False Then
            ' Bitmapに直接アクセスするためのオブジェクト開放(UnlockBits)
            _bmp.UnlockBits(_img)
            _img = Nothing
        End If
    End Sub

    ''' <summary>
    ''' BitmapのGetPixel同等
    ''' </summary>
    ''' <param name="x">X座標</param>
    ''' <param name="y">Y座標</param>
    ''' <returns>Colorオブジェクト</returns>
    Public Function GetPixel(ByVal x As Integer, ByVal y As Integer) As Color
        If _img Is Nothing = True Then
            ' Bitmap処理の高速化を開始していない場合はBitmap標準のGetPixel
            Return _bmp.GetPixel(x, y)
        End If
        Dim adr As IntPtr = _img.Scan0
        Dim pos As Integer = x * 3 + _img.Stride * y
        Dim b As Byte = System.Runtime.InteropServices.Marshal.ReadByte(adr, pos + 0)
        Dim g As Byte = System.Runtime.InteropServices.Marshal.ReadByte(adr, pos + 1)
        Dim r As Byte = System.Runtime.InteropServices.Marshal.ReadByte(adr, pos + 2)
        Return Color.FromArgb(r, g, b)
    End Function


    '''// <summary>
    ''' BitmapのSetPixel同等
    ''' </summary>
    ''' <param name="x">X座標</param>
    ''' <param name="y">Y座標</param>
    ''' <param name="col">Colorオブジェクト</param>
    Public Sub SetPixel(ByVal x As Integer, ByVal y As Integer, ByVal col As Color)
        If _img Is Nothing = True Then
            ' Bitmap処理の高速化を開始していない場合はBitmap標準のSetPixel
            _bmp.SetPixel(x, y, col)
            Return
        End If

        ' Bitmap処理の高速化を開始している場合はBitmapメモリへの直接アクセス
        Dim adr As IntPtr = _img.Scan0
        Dim pos As Integer = x * 3 + _img.Stride * y
        System.Runtime.InteropServices.Marshal.WriteByte(adr, pos + 0, col.B)
        System.Runtime.InteropServices.Marshal.WriteByte(adr, pos + 1, col.G)
        System.Runtime.InteropServices.Marshal.WriteByte(adr, pos + 2, col.R)
    End Sub
End Class

★テスト用フォーム(Form1.vb)
Public Class Form1
    Private Sub Button1_Click( _
        ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        ' Bitmapオブジェクト生成
        Dim bmp As Bitmap
        If PictureBox1.Image Is Nothing = True Then
            ' Bitmapを新規生成
            bmp = New Bitmap(512, 512)
        Else
            ' BitmapをpictureBoxから取得
            bmp = New Bitmap(PictureBox1.Image)
        End If

        ' Bitmap処理の高速化開始
        Dim bmpP As BitmapPlus = New BitmapPlus(bmp)
        bmpP.BeginAccess()
        For i As Integer = 0 To bmp.Width - 1
            For j As Integer = 0 To bmp.Height - 1
                ' Bitmapの色取得
                'Dim bmpCol As Color = bmp.GetPixel(i, j)
                Dim bmpCol As Color = bmpP.GetPixel(i, j)
                If bmpCol.R = 0 Then
                    ' Bitmapの色設定
                    'bmp.SetPixel(i, j, Color.FromArgb(255, 255, 0, 0))
                    bmpP.SetPixel(i, j, Color.FromArgb(255, 255, 0, 0))
                Else
                    'bmp.SetPixel(i, j, Color.FromArgb(255, 0, 0, 0))
                    bmpP.SetPixel(i, j, Color.FromArgb(255, 0, 0, 0))
                End If
            Next
        Next
        ' Bitmap処理の高速化終了
        bmpP.EndAccess()
        PictureBox1.Image = bmp
    End Sub
End Class