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