■ 用語整理
ストライド(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
■ BitmapData
https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.imaging.bitmapdata?view=netframework-4.8Scan0 プロパティ
* ビットマップ内の最初のピクセル データのアドレスhttps://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.imaging.bitmapdata.scan0?view=netframework-4.8
Stride プロパティ
* Bitmap オブジェクトのストライド幅 (スキャン幅とも呼ばれる)https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.imaging.bitmapdata.stride?view=netframework-4.8
■ サンプル
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); } } } }
参考文献
http://neareal.com/470/https://terasb.blogspot.com/2017/07/bitmapstride.html
http://neareal.net/index.php?ComputerGraphics%2FImageProcessing%2FTheStructureOfImageData
https://melpon.hatenadiary.org/entry/20061128/p1
https://imagingsolution.net/program/csharp/bitmap_class_pointer/
https://imagingsolution.net/program/csharp/bitmap-data-memory-format/
https://imagingsolution.net/program/csharp/setgetpixel/
https://qiita.com/Tachibana446/items/31cdda5cac78cf571a04
http://blog.livedoor.jp/ha_yshr/archives/51230409.html
https://ijupiter.hatenadiary.org/entry/20090213/p1
https://blogs.yahoo.co.jp/spike_spike690/4735887.html
https://tocsworld.wordpress.com/2014/05/13/c%E3%81%A7%E3%81%AE%E7%94%BB%E5%83%8F%E5%87%A6%E7%90%86-%E5%9C%9F%E5%8F%B0%E7%B7%A8/
https://www.84kure.com/blog/2014/07/13/c-%E3%83%93%E3%83%83%E3%83%88%E3%83%9E%E3%83%83%E3%83%97%E3%81%AB%E3%83%94%E3%82%AF%E3%82%BB%E3%83%AB%E5%8D%98%E4%BD%8D%E3%81%A7%E9%AB%98%E9%80%9F%E3%81%AB%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9/
https://dobon.net/vb/dotnet/graphics/drawnegativeimage.html
http://espresso3389.hatenablog.com/entry/20090421/1240315594
https://symfoware.blog.fc2.com/blog-entry-2195.html
http://koshinran.hateblo.jp/entry/2018/11/13/210754
Bitmap <=> byteの相互変換
https://www.ipentec.com/document/csharp-bmp-to-byte
https://www.ipentec.com/document/csharp-byte-to-bmp