VB.NET2005 TIPS / グラフィックス系 |
G0505 画像の高速処理化 |
最終更新:2007/02/21 全面改訂 |
●解説
ビットマップでは補間を伴うアフィン変換は既にネイティブに対応されていて十分高速である。これは、VBでもC#でも同じである。しかし、色変換(濃度変換)系統は、VBではSetPixel、GetPixelと言う恐ろしく遅い関数しかない。VB6に比べると随分高速になったが、それでもインタラクティブな処理(ユーザのリアルタイムなパラメータ変更に追従するような処理)には向いていない。C#で
も同じである。基本的に、VB.NETとC#.NETはほぼ同じパフォーマンスと考えて良い。
ここでは、ビットマップをメモリ上のデータ群として高速に処理する方法を紹介する。
●原理
VB.NETもC#.NETもプログラマにメモリアドレス(ポインタ)の概念を忘れさせる方式を取っている。ところが、高速にメモリをアクセスするには、やはりポインタにより直接メモリを読み書きするしかない。実は、.NETでは以下のような手法が用意されている。
-
ビットマップのイメージをメモリ上のデータとして見せる手段(BitmapDataオブジェクト)
-
メモリをポインタで自由に読み書きできる手段(Marshal)
-
更に、メモリデータを配列化する手段を提供している。
である。アンマネージ型とマネージ型の変換となる(アセンブラの世界と同じアクセスで、筆者はこの方が馴染み良い)。これらを利用すれば高速にビットマップを処理できる。但し、ポインタはアンマネージなので、プログラマは最新の注意が必要となる。C#でも基本的にポインタを排除しているので、アンマネージ型処理となる。
●方法
1.ビットマップのイメージをメモリ上のデータとする方法
ビットマップのLockBitsメソッドで指定の矩形領域をメモリ上のデータに割り付け、これをBitmapDataオブジェクトとする。BitmapDataのScan0プロパティが目的のメモリアドレスの先頭となる。
2.メモリ(ピクセルデータ)をポインタで自由に読み書きする方法
Marshalクラスにて特定のデータ型で読み書きする関数でピクセルの要素(R、G、B)を直接アクセスする。
ReadByte(ptr, Offset, data)
WriteByte(ptr, Offset, data)
ptr:ポインタ構造体
Offset:ptrからのオフセット値
data:バイトデータ
などで、自由にピクセルデータを処理できる。メモリ上の画像の物理的な幅は、Stride(バイト単位)で分かる。
3.メモリデータを配列化する方法
Marshal
にはCopyメソッドがあり、メモリ上のポインターで指されたところから任意の語数を、ローカルな配列にコピーしたり、その配列をメモリ上のポインターへ戻したりできる。ローカルな配列では高速な処理が可能となる。
4.ビットマップを元に戻す
ビットマップのUnlockBitsメソッドでLock状態を開放する。これをして置かないとマネージ側からアクセスできない。
●実例
○Marshal WriteByte
Imports System.Runtime.InteropServices.Marshal
Dim bP As IntPtr
bmp = New Bitmap(Sz, Sz, Drawing.Imaging.PixelFormat.Format24bppRgb) '3バイト/ピクセル
Dim BD As Imaging.BitmapData
BD = bmp.LockBits(New Rectangle(0, 0,
bmp.Width, bmp.Height), Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)
bP = BD.Scan0
For j = 0 To Sz - 1
For k = 0 To Sz - 1
ofs = j * Sz * 3 + k *
3
WriteByte(bP,
ofs + 0, 11)
WriteByte(bP, ofs + 1, 12)
WriteByte(bP, ofs + 2, 13)
Next
Next
bmp.UnlockBits(BD) 'メモリデータをビットマップに戻す
○Marshal 配列化
Imports System.Runtime.InteropServices.Marshal
Dim bP As IntPtr
bmp = New Bitmap(Sz, Sz, Drawing.Imaging.PixelFormat.Format24bppRgb)
Dim BD As Imaging.BitmapData
BD = bmp.LockBits(New Rectangle(0, 0,
bmp.Width, bmp.Height), Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat)
bP = BD.Scan0
Dim p As Integer
Dim BC As Integer = Sz * Sz * 3
Dim BB(BC - 1) As Byte
Copy(bP, BB, 0,
BC) '指定されたポインタから自分の配列にコピーする
For j = 0 To Sz - 1
For k = 0 To Sz - 1
p = j * Sz * 3 + k * 3
BB(p) =
11
'後は自由に演算できる
BB(p + 1) = 12
BB(p + 2) = 13
Next
Next
Copy(BB, 0, bP, BC) '指定されたポインタへ、配列を戻す
bmp.UnlockBits(BD) 'メモリデータをビットマップに戻す
|