【C#】Bitmap / BitmapData

■ 用語整理

ストライド(Stride)

 * 画像のような二次元配列の構造のものを、一次元配列で扱う場合、
   配列の何番目から何番目までのデータが画像の横一列分に相当するのかを表す値
 => 以下のサイトが非常に分かりやすい
http://neareal.com/470/

例:画像幅が 512px でフォーマットが BGRA 32bit の画像の場合
 => ストライドは 512px * 4byte(= 32bit) = 2048byte

# BGRA : Blue(青)/Green(緑)/Red(赤)/Alpha(透明度)

アライメント(Alignment)

 * メモリ上でデータの境界がそろえられること
https://www.itmedia.co.jp/enterprise/articles/0506/14/news003_3.html


■ サンプル

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      if (this.pictureBox1.Image != null)
      {
        this.pictureBox1.Image.Dispose();
      }

      Bitmap bitmap = new Bitmap(@"20161215052204.gif");
      
      // ロックしてポインタ参照できるようにする
      BitmapData bitmapData = bitmap.LockBits(
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
        ImageLockMode.ReadWrite, bitmap.PixelFormat);

      // ポインタを渡してBitmapクラスを作成(BitmapDataのSacn0 => メモリの先頭アドレス)
      IntPtr pointer = bitmapData.Scan0;
      // 処理後の画像データ用配列
      byte[] pixels = new byte[Math.Abs(bitmapData.Stride) * bitmapData.Height];
      // Bitmapデータを配列へコピー
      Marshal.Copy(pointer, pixels, 0, pixels.Length);

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

      // 配列をBitmapデータへコピー
      Marshal.Copy(pixels, 0, pointer, pixels.Length);
      // アンロック
      bitmap.UnlockBits(bitmapData);

      this.pictureBox1.Image = bitmap;
    }

    private void button2_Click(object sender, EventArgs e)
    {
      if (this.pictureBox1.Image != null)
      {
        this.pictureBox1.Image.Dispose();
      }

      Bitmap bitmap = new Bitmap(@"20161215052204.gif");
      BitmapData bitmapData = bitmap.LockBits(
          new Rectangle(0, 0, bitmap.Width, bitmap.Height),
          ImageLockMode.ReadWrite,
          PixelFormat.Format32bppArgb);
      // ポインタを渡してBitmapクラスを作成(BitmapDataのSacn0 => メモリの先頭アドレス)
      IntPtr pointer = bitmapData.Scan0;
      int bytes = bitmap.Width * bitmap.Height * 4;
      for (int i = 0; i < bytes; i += 4)
      {
        byte red = Marshal.ReadByte(pointer, i);
        byte green = Marshal.ReadByte(pointer, i + 1);
        byte blue = Marshal.ReadByte(pointer, i + 2);

        //青、緑、赤の色を変更する
        Marshal.WriteByte(pointer, i, (byte)(255 - red));
        Marshal.WriteByte(pointer, i + 1, (byte)(255 - green));
        Marshal.WriteByte(pointer, i + 2, (byte)(255 - blue));
      }
      bitmap.UnlockBits(bitmapData);

      this.pictureBox1.Image = bitmap;
    }
  }
}

■ Bitmap <=> byte[]の相互変換

using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;

namespace SampleForm
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      Bitmap targetBitmap = new Bitmap(@"20161215052204.gif");
      byte[] imageBytes = ToBytes(targetBitmap, ImageFormat.Bmp);
      Bitmap cloneBitmap = ToBitmap(imageBytes);
      this.pictureBox1.Image = cloneBitmap;
    }

    public static byte[] ToBytes(Bitmap targetBitmap, ImageFormat imageFormat)
    {
      using (MemoryStream memoryStream = new MemoryStream())
      {
        targetBitmap.Save(memoryStream, imageFormat);
        return memoryStream.GetBuffer();
      }
    }

    public static Bitmap ToBitmap(byte[] imageBytes)
    {
      using (MemoryStream memoryStream = new MemoryStream(imageBytes))
      {
        return new Bitmap(memoryStream);
      }
    }
  }
}


関連記事

C#】Bitmap の高速化 について

https://blogs.yahoo.co.jp/dk521123/38088957.html

C#】Byte / Byte (Byteの配列)

https://blogs.yahoo.co.jp/dk521123/38080788.html

C#】画像かどうかを判定する ~ ImageCodecInfo / Bitmap を使って ~

https://blogs.yahoo.co.jp/dk521123/24965866.html

C#】アンセーフ コード ~ unsafe / fixed ~

https://blogs.yahoo.co.jp/dk521123/38082656.html

C#】IntPtr構造体

https://blogs.yahoo.co.jp/dk521123/28757641.html

C#】二値化

https://blogs.yahoo.co.jp/dk521123/38086531.html