【C#】Bitmapクラスへのポインタ渡し

画像処理のプログラムを作成するときは、GUIはC#、画像処理はC、C++で書かれたライブラリで作成するというのが、私の定番となっているのですが、C言語で処理された画像データをC#へ渡す場合は、画像データのコピーや画像ファイルを介せずとも、画像データのポインタをC#側へ渡す事で画像データを扱うことが可能となります。

 

これを可能にするのには、Bitmapクラスのコンストラクタに

public Bitmap(
    int width,
    int height,
    int stride,
    PixelFormat format,
    IntPtr scan0
)

というのがあるので、scan0の部分にC言語で扱っている画像データのポインタを渡す事で、C#のビットマップオブジェクトとして扱うことが可能となります。

ただし、画像データのメモリ構造はトップダウンで画像1行あたりのサイズが4バイト単位となります。

(参考)

【C#】Bitmap画像データのメモリ構造

 

scan0に渡されたポインタはBitmapクラス内ではメモリを参照するだけ(Bitmapクラス内でメモリを確保していない)なので、BitmapオブジェクトをDisposeするまでscan0で示されたメモリは解放しないように注意が必要です。

逆にBitmapオブジェクトをDisposeしてもメモリは解放されないので、メモリの解放も忘れずに。

 

メモリが参照されているだけかどうか?を評価するのに、下記のようなプログラムを作成してみました。

// 元のBitmap
var bmp1 = new Bitmap(@"C:\Temp\Lenna.bmp");

// ロックしてポインタ参照できるようにする
var bmpData1 = bmp1.LockBits(new Rectangle(0, 0, bmp1.Width, bmp1.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp1.PixelFormat);

// ポインタを渡してBitmapクラスを作成
var bmp2 = new Bitmap(
    bmp1.Width,
    bmp1.Height,
    bmpData1.Stride,
    bmp1.PixelFormat,
    bmpData1.Scan0 // bmp1の画像データのポインタ
);

// アンロック
bmp1.UnlockBits(bmpData1);

// ロックしてポインタ参照できるようにする
var bmpData2 = bmp2.LockBits(new Rectangle(0, 0, bmp2.Width, bmp2.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp2.PixelFormat);

var data2 = new byte[Math.Abs(bmpData2.Stride) * bmp2.Height];

// bmpData2の輝度値を配列へコピーする
System.Runtime.InteropServices.Marshal.Copy(
    bmpData2.Scan0,
    data2,
    0,
    data2.Length
);

// 画像の輝度値を+50する
for (int i = 0; i < data2.Length; i++)
{
    data2[i] = (byte)Math.Min(data2[i] + 50, 255);
}

// 元のポインタへコピーして、データを戻す
System.Runtime.InteropServices.Marshal.Copy(
    data2,
    0,
    bmpData2.Scan0,
    data2.Length
);

// アンロック
bmp2.UnlockBits(bmpData2);
//
bmp2.Dispose();

// bmp1を保存(※bmp1は直接輝度値の変更はしていない)
bmp1.Save(@"C:\Temp\Lenna1.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
// 解放
bmp1.Dispose();

上記のプログラムではbmp1でBitmapオブジェクトを作成(画像のメモリを確保)し、bmp1の画像データのポインタをbmp2へ渡しています。

この時点でbmp1とbmp2の画像データのメモリは同じをメモリを参照しているはずなので、bmp2の画像の輝度値を変更すると、bmp1の画像も変更されているか?を確認しています。

 

元の画像(bmp1の画像)

 

bmp2を処理した後のbmp1の画像

 

以上のようになり、思惑通りでした!

 

画像処理のためのC#テクニックへ戻る

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください