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 records = new List() { 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();
}
相關文章