C#中如何高性能地把图像转换成grayscale的

C#里把图片灰度化: 先是用以下代码, 能转换, 但经测试性能比较低, 在窗口中预览灰度化的采集视频, 相当卡顿, 这是因为在两重循环里大量调用GetPixel和SetPixel

        private Bitmap rgb2gray(Bitmap bm)
        {
            //Row-wise iteration through the Bitmap 
            for (int y = 0; y < bm.Height; y++)
            {
                for (int x = 0; x < bm.Width; x++)
                {
                    Color pixelColor = bm.GetPixel(x, y);
 
                    int pixelLuminance = (int)(pixelColor.R * 0.2126 + pixelColor.G * 0.7152 + pixelColor.B * 0.0722);
 
                    bm.SetPixel(x, y, Color.FromArgb(pixelLuminance, pixelLuminance, pixelLuminance));
                }//for
            }//for
 
            return bm;
        }//rgb2gray(Bitmap)

经搜索, 在http://stackoverflow.com/questions/1580130/high-speed-performance-of-image-filtering-in-c-sharp 里找到以下代码: 

public static void GrayScaleImage(Bitmap image)
{
    if (image == null)
        throw new ArgumentNullException("image");

    // lock the bitmap.
    var data = image.LockBits(
                  new Rectangle(0, 0, image.Width, image.Height), 
                  ImageLockMode.ReadWrite, image.PixelFormat);
    try
    {
        unsafe
        {
            // get a pointer to the data.
            byte* ptr = (byte*)data.Scan0;

            // loop over all the data.
            for (int i = 0; i < data.Height; i++)
            {
                for (int j = 0; j < data.Width; j++)
                {
                    // calculate the gray value.
                    byte y = (byte)(
                        (0.299 * ptr[2]) + 
                        (0.587 * ptr[1]) + 
                        (0.114 * ptr[0]));

                    // set the gray value.
                    ptr[0] = ptr[1] = ptr[2] = y;

                    // increment the pointer.
                    ptr += 3;
                }

                // move on to the next line.
                ptr += data.Stride - data.Width * 3;
            }
        }
    }
    finally
    {
        // unlock the bits when done or when 
        // an exception has been thrown.
        image.UnlockBits(data);
    }
}

 改用以上代码, 经测试, 性能可以, 但是处理后的图片有些问题, 左边的大部分灰度化的区域有栅格, 右边有一小段区域没有灰度化.  这当然难不倒老杨,  老杨感觉是ptr指向的索引不正确的问题,  参考了下其他代码(https://github.com/baobaohuang/Graphic_gray/blob/cd1d9b543762c23a8fc232e21f8de15dd810e8b2/gray/Program.cs  , 这个也可以工作, 性能高于最初的代码,但低于上面的代码),  修正代码如下: 

        public static void GrayScaleImage(Bitmap image)
        {
            if (image == null)
                throw new ArgumentNullException("image");

            // lock the bitmap.
            var data = image.LockBits(
                          new Rectangle(0, 0, image.Width, image.Height),
                          ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            try
            {
                unsafe
                {
                    // get a pointer to the data.
                    byte* ptr = (byte*)data.Scan0;

                    // loop over all the data.
                    for (int i = 0; i < data.Height; i++)
                    {
                        ptr = (byte*)data.Scan0 + i*data.Stride;
                        for (int j = 0; j < data.Width; j++)
                        {
                            // calculate the gray value.
                            byte y = (byte)(
                                (0.299 * ptr[2]) +
                                (0.587 * ptr[1]) +
                                (0.114 * ptr[0]));

                            // set the gray value.
                            ptr[0] = ptr[1] = ptr[2] = y;

                            // increment the pointer.
                            ptr += 3;
                        }

                    }
                }
            }
            finally
            {
                // unlock the bits when done or when 
                // an exception has been thrown.
                image.UnlockBits(data);
            }
        }

最后测试, 以上代码效果perfect !  不过后来发现, 其实两段代码的处理ptr的方式都一样可以, 主要是

var data = image.LockBits(
                          new Rectangle(0, 0, image.Width, image.Height),
                          ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

 这里必须用PixelFormat.Format24bppRgb

猜你喜欢

转载自wooce.iteye.com/blog/2312620