C#ビットマップとビットマップデータの理解



C Bitmap Bitmapdata Understanding

多くの画像処理タスクは、32ビット深度から8ビット深度フォーマットへの変換など、最も単純なファイルタイプ変換であり、ピクセル配列への直接アクセスは、GetPixelやSetPixelを使用するよりもはるかに効率的です。

DotNetがホスティングメカニズムを使用していることに気付くかもしれません。ほとんどの場合、Microsoftは、便利で安全な理由から、マネージコードを使用することをお勧めします。実際には、メモリ内のデータブロックを直接操作することはめったにありません。ただし、マネージコードを使用することの非効率性は、特に大量の場合には耐えられないため、画像処理は数少ないケースの1つにすぎません。画像のために、ここでは新しいアプローチについて説明します。



アンマネージコードの使用方法は言語によって異なります。 C#では、unsafeキーワードを使用してポインターを呼び出し、メモリ内のビットマップデータを直接操作できます。 VBはMarshalクラスのメソッドを使用するため、パフォーマンスが向上します。損失があるため、効率は前者ほど良くありません。

ビットストリームをロックする

Bitmapクラスは、LockBitsメソッドとUnLockBitsメソッドを使用して、ビットマップのデータマトリックスをメモリに格納します。直接操作する最後に、変更されたデータは、ビットマップ内の元のデータを置き換えるために使用されます。 LockBitsは、各BitmapDataのクラスとともに、ロックされた行列内の記述されたデータの位置と分布を返します。



BitmapDataクラスには、次の重要なプロパティが含まれています。

  • Scan0:メモリ内のデータマトリックスのアドレス。
  • ストライド:データマトリックスの線幅(バイト単位)。数バイト拡張される可能性がありますが、これについては後で紹介します。
  • PixelFormat:マトリックス内のバイトの配置に重要なピクセル形式。
  • 幅:ビットマップの幅。
  • 高さ:ビットマップの高さ。
    具体的な関係を次の図に示します。
    画像

図1に示すように、 stride属性は、ビットマップデータマトリックスの線幅を表しますバイト単位 。効率上の理由から、行列の線幅は1行あたりのピクセル数の整数倍ではなく、システムはそれを4の整数倍にカプセル化する傾向があります。たとえば、深さ24ビットの17ピクセル幅の画像の場合、ストライド属性は52で、1行あたりのデータ量は17です。 3 = 51の場合、システムは自動的に1バイトをカプセル化するため、そのストライドは52バイト(または13バイト)になります。 4バイト)。 17ピクセル幅の4ビットインデックスマップの場合、ストライドは12で、そのうち9バイト(正確には8.5バイト)がデータ情報の記録に使用され、各行に3(3.5)バイトが自動的に追加されます。 4.整数倍。
特定のデータの分布は、そのピクセル形式によって異なります。 24ビットの深さの画像には3バイトごとにRGB情報のセットが含まれます。32ビットの深さの画像には4バイトごとにRGBA情報のセットが含まれます。 4ビットのインデックス付き画像や1ビットのインデックス付き画像など、バイトごとに複数のピクセルを含むピクセル形式は、同じバイト内の隣接するバイトが混同されないように注意深く処理する必要があります。
ポインタの正確な配置
32ビットRGB:XとYがビットマップ内のピクセルの座標であるとすると、メモリ内のそのアドレスはscan0 + Yです。 stride + X 4.この時点で、ポインターは青を指し、その後に緑、赤、およびアルファのコンポーネントが続きます。
24ビットRGB:scan0 + Y stride + X 3.ポインタが青を指し、続いて緑と赤を指します。
8ビットインデックス:scan0 + Y ストライド+ X。現在のポインタは画像のパレットを指しています。
4ビットインデックス:scan0 + Y
ストライド+(X / 2)。現在のポインタが指すバイトには2つのピクセルが含まれ、16トーンのカラーホイールは上位ビットと下位ビットによってインデックスが付けられます。上位ビットは左側のピクセルを表し、下位ビットは右側のピクセルを表します。
1ビットインデックス:scan0 + Y * stride + X / 8。現在のポインタが指すバイトの各ビットは、1ピクセルのインデックスカラーを表し、パレットは2色、左端のピクセルは8、右端のピクセルはゼロです。
ピクセル間でイテレータを使用する
次の例では、32ビットの深さの画像のすべてのピクセルの青の成分を最大(255)に設定します。
BitmapData bmd = bm.LockBits(new Rectangle(0、0、10、10)、System.Drawing.Imaging.ImageLockMode.ReadOnly、bm.PixelFormat)
int PixelSize = 4

for(int y=0 y > 1) byte currentByte = ((byte *)bmd.Scan0)[offset] if((x&1) == 1) currentByte &= 0xF0 currentByte else currentByte &= 0x0F currentByte ((byte *)bmd.Scan0)[offset]=currentByte Handling the code for a 1-bit index: byte* p=(byte*)bmd.Scan0.ToPointer() int index=y*bmd.Stride+(x>>3) byte mask=(byte)(0x80>>(x&0x7)) if(pixel) p[index]|=mask else p[index]&=(byte)(mask^0xff) Finally, after all the processing is done, don't forget to use the Unlockbits command to unlock.