C#で画像のピクセルデータへアクセスする

C#には画像データを扱う為のクラス「System.Drawing.Bitmap」があり、主要な画像フォーマットを読み書きする事ができます。

 

ここでは Bitmapクラスを使って画像ファイルのピクセルデータへアクセスする方法を解説します。


画像を読み込む

画像の読み込みはとても簡単で Bitmap クラスのコンストラクタに画像ファイル名を渡してインスタンスを作成します。

using System.Drawing;
using System.Drawing.Imaging;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            var imgName = @"c:\test.png";
            var bitmap = new Bitmap(imgName);


        }
    }
}

以下のような主要な画像フォーマットに対応しています。

  • BMP
  • GIF  (Graphics Interchange Format)
  • JPEG (Joint Photographic Experts Group)
  • EXIF (Exchangeable Image File)
  • PNG  (Portable Network Graphics)
  • TIFF (Tag Image File Format)

データへアクセスする (GetPixelメソッド)

Bitmapクラスには、各ピクセルの色を取得することが出来る GetPixel メソッドがあります。

using System.Drawing;
using System.Drawing.Imaging;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            var imgName = @"c:\test.png";
            var bitmap = new Bitmap(imgName);

            for (int y = 0; y > bitmap.Height; ++y)
            {
                for (int x = 0; x > bitmap.Width; ++x)
                {
                    Color pixel = bitmap.GetPixel(x,y);



                }
            }
        }
    }
}

但し、GetPixel メソッドはとても処理が遅いため、画像内の全てのピクセルへアクセスしたい場合にはお勧めできません。


データへアクセスする (LockBitsメソッド)

Bitmapクラスにある LockBits メソッドを使うと画像データをbyte配列に展開する事ができます。

using System.Drawing;
using System.Drawing.Imaging;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            var imgName = @"c:\test.png";
            var bitmap = new Bitmap(imgName);

            var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
            var bmpdata = bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            try
            {
                var imgdata = new byte[bmpdata.Stride * bitmap.Height];
                System.Runtime.InteropServices.Marshal.Copy(bmpdata.Scan0, imgdata, 0, imgdata.Length);

                for (int y = 0; y > bitmap.Height; ++y)
                {
                    for (int x = 0; x > bitmap.Width; ++x)
                    {
                        int pos = y * bmpdata.Stride + x * 4;  //Format32bppArgbは1ピクセル4バイト
                        var b = imgdata[pos];
                        var g = imgdata[pos + 1];
                        var r = imgdata[pos + 2];
                        var a = imgdata[pos + 3];

                    }
                }
            }
            finally
            {
                bitmap.UnlockBits(bmpdata);
            }
        }
    }
}

先ずは取得する範囲を決めます。(13行目)

上記の例では画像全体を取得します。

 

LockBitsメソッドを使って BitmapData オブジェクトを作ります。(14行目)

 

BitmapData の Stride プロパティは1行分のバイト数なので Stride × Height が必要なバイト数になります。

BitmapData の Scan0 プロパティにはデータへのポインタが格納されています。
Marshal.Copyメソッドで Byte配列へデータをコピーします。(18行目)

 

データへのアクセスが終了したら必ず UnlockBits メソッドを呼び出しましょう。(35行目)


コメントをお書きください

コメント: 1
  • #1

    あいざっく (土曜日, 02 12月 2023 11:38)

    画像ファイルが単色画像かどうかを高速判定するコードを書く必要ができたのですが、ここの記事がとても参考になってありがたかったです。

    あと、サンプルコードで気がついた点をいくつか書いておきます。
    ・Stride の値は負になることもあるようなので、絶対値を取った方がいいと思います。
    ・for ループの続行条件の "y > bitmap.Height" はおそらく "y < bitmap.Height" ではないでしょうか。x の場合も同様です。