C# 快速 彩色(RGB) 轉灰階
C# 快速 彩色(RGB) 轉灰階
資料來源: https://blog.holey.cc/2018/04/18/csharp-three-way-covert-bmp-rgb-to-gray/
1. GetPixel/SetPixel
static Bitmap RGBtoGray(Bitmap srcbmp) { Bitmap bitmap = new Bitmap(bmpSrc); for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { int gray = ( bitmap.GetPixel(x, y).R + bitmap.GetPixel(x, y).G + bitmap.GetPixel(x, y).B) / 3; Color color = Color.FromArgb(gray, gray, gray); bitmap.SetPixel(x, y, color); } } return bitmap; }
2. unsafe 指標法
static Bitmap RGBtoGray(Bitmap srcbmp) { Bitmap bitmap = new Bitmap(bmpSrc); BitmapData bmpData = bitmap.LockBits( new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); unsafe { int stride = bmpData.Stride; int offset = stride - bitmap.Width * 3; IntPtr intPtr = bmpData.Scan0; byte* p = (byte*)(void*)intPtr; for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { byte gray = (byte)(( p[y * (bitmap.Width * 3 + offset) + (x * 3) + 0] + p[y * (bitmap.Width * 3 + offset) + (x * 3) + 1] + p[y * (bitmap.Width * 3 + offset) + (x * 3) + 2]) / 3); for (int i = 0; i < 3; i++) { p[y * (bitmap.Width * 3 + offset) + (x * 3) + i] = gray; } } } } bitmap.UnlockBits(bmpData); return bitmap; }
3. Graphics 重繪法
static Bitmap RGBtoGray(Bitmap bmpSrc) { ColorMatrix cm = new ColorMatrix(new float[][] { new float[] { 0.30f, 0.30f, 0.30f, 0.00f, 0.00f } , new float[] { 0.59f, 0.59f, 0.59f, 0.00f, 0.00f } , new float[] { 0.11f, 0.11f, 0.11f, 0.00f, 0.00f } , new float[] { 0.00f, 0.00f, 0.00f, 1.00f, 0.00f } , new float[] { 0.00f, 0.00f, 0.00f, 0.00f, 1.00f } }); ImageAttributes ia = new ImageAttributes(); ia.SetColorMatrix(cm); Bitmap bitmap = new Bitmap(bmpSrc.Width, bmpSrc.Height, bmpSrc.PixelFormat); Graphics g = Graphics.FromImage(bitmap); Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); g.DrawImage(bmpSrc, rect, 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, ia); return bitmap; }
測試:
static void Main(string[] args) { Stopwatch sw = new Stopwatch(); List<long> records = new List<long>() { 0, 0, 0 }; Bitmap bitmap = new Bitmap("input.bmp"); Console.WriteLine("========== Bitmap Info =========="); Console.WriteLine( $"Filename\t{ "input.bmp"} " + Environment.NewLine + $"Palette \t{ Image.GetPixelFormatSize(bitmap.PixelFormat) }" + Environment.NewLine + $"Width \t{ bitmap.Height }" + Environment.NewLine + $"Height \t{ bitmap.Width }" + Environment.NewLine); for (int i = 0; i < 10; i++) { Console.WriteLine($"========== Count { i } =========="); sw = Stopwatch.StartNew(); RGBtoGray1(new Bitmap("input.bmp")); sw.Stop(); records[0] += sw.ElapsedMilliseconds; Console.WriteLine(string.Format("Method 1 cost {0,5} milliseconds", sw.ElapsedMilliseconds)); sw.Reset(); sw = Stopwatch.StartNew(); RGBtoGray2(new Bitmap("input.bmp")); sw.Stop(); records[1] += sw.ElapsedMilliseconds; Console.WriteLine(string.Format("Method 2 cost {0,5} milliseconds", sw.ElapsedMilliseconds)); sw = Stopwatch.StartNew(); RGBtoGray3(new Bitmap("input.bmp")); sw.Stop(); records[2] += sw.ElapsedMilliseconds; Console.WriteLine(string.Format("Method 3 cost {0,5} milliseconds", sw.ElapsedMilliseconds)); Console.WriteLine(); } Console.WriteLine("========== Average =========="); Console.WriteLine(string.Format("Method 1: {0, 7} milliseconds", (float)records[0] / 10)); Console.WriteLine(string.Format("Method 2: {0, 7} milliseconds", (float)records[1] / 10)); Console.WriteLine(string.Format("Method 3: {0, 7} milliseconds", (float)records[2] / 10)); Console.ReadKey(); }