摘自:http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp#_rating
当你在C#中操作bitmaps时,你可以使用 GetPixel(x ,y) 和 SetPixel(x, y, color) 去获取/设置特定像素的值。但是它们十分的慢。
这里有另一种更快的方法去操作bitmaps。
LockBitmap
使用LockBitmap类,我们可以锁定/解锁bitmaps的数据
(PS:这里定义一个类LockBitmap,锁定时把bitmap所有像素的数据拷贝在一个数组上,操作时就是在数组上直接操作,避免了对bitmap的直接操作,解锁后就拷贝回到bitmap中。)
1 public class LockBitmap 2 { 3 Bitmap source = null; 4 IntPtr Iptr = IntPtr.Zero; 5 BitmapData bitmapData = null; 6 7 public byte[] Pixels { get; set; } 8 public int Depth { get; private set; } 9 public int Width { get; private set; } 10 public int Height { get; private set; } 11 12 public LockBitmap(Bitmap source) 13 { 14 this.source = source; 15 } 16 17 /// <summary> 18 /// Lock bitmap data 19 /// </summary> 20 public void LockBits() 21 { 22 try 23 { 24 // Get width and height of bitmap 25 Width = source.Width; 26 Height = source.Height; 27 28 // get total locked pixels count 29 int PixelCount = Width * Height; 30 31 // Create rectangle to lock 32 Rectangle rect = new Rectangle(0, 0, Width, Height); 33 34 // get source bitmap pixel format size 35 Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat); 36 37 // Check if bpp (Bits Per Pixel) is 8, 24, or 32 38 if (Depth != 8 && Depth != 24 && Depth != 32) 39 { 40 throw new ArgumentException("Only 8, 24 and 32 bpp images are supported."); 41 } 42 43 // Lock bitmap and return bitmap data 44 bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite, 45 source.PixelFormat); 46 47 // create byte array to copy pixel values 48 int step = Depth / 8; 49 Pixels = new byte[PixelCount * step]; 50 Iptr = bitmapData.Scan0; 51 52 // Copy data from pointer to array 53 Marshal.Copy(Iptr, Pixels, 0, Pixels.Length); 54 } 55 catch (Exception ex) 56 { 57 throw ex; 58 } 59 } 60 61 /// <summary> 62 /// Unlock bitmap data 63 /// </summary> 64 public void UnlockBits() 65 { 66 try 67 { 68 // Copy data from byte array to pointer 69 Marshal.Copy(Pixels, 0, Iptr, Pixels.Length); 70 71 // Unlock bitmap data 72 source.UnlockBits(bitmapData); 73 } 74 catch (Exception ex) 75 { 76 throw ex; 77 } 78 } 79 80 /// <summary> 81 /// Get the color of the specified pixel 82 /// </summary> 83 /// <param name="x"></param> 84 /// <param name="y"></param> 85 /// <returns></returns> 86 public Color GetPixel(int x, int y) 87 { 88 Color clr = Color.Empty; 89 90 // Get color components count 91 int cCount = Depth / 8; 92 93 // Get start index of the specified pixel 94 int i = ((y * Width) + x) * cCount; 95 96 if (i > Pixels.Length - cCount) 97 throw new IndexOutOfRangeException(); 98 99 if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha 100 { 101 byte b = Pixels[i]; 102 byte g = Pixels[i + 1]; 103 byte r = Pixels[i + 2]; 104 byte a = Pixels[i + 3]; // a 105 clr = Color.FromArgb(a, r, g, b); 106 } 107 if (Depth == 24) // For 24 bpp get Red, Green and Blue 108 { 109 byte b = Pixels[i]; 110 byte g = Pixels[i + 1]; 111 byte r = Pixels[i + 2]; 112 clr = Color.FromArgb(r, g, b); 113 } 114 if (Depth == 8) 115 // For 8 bpp get color value (Red, Green and Blue values are the same) 116 { 117 byte c = Pixels[i]; 118 clr = Color.FromArgb(c, c, c); 119 } 120 return clr; 121 } 122 123 /// <summary> 124 /// Set the color of the specified pixel 125 /// </summary> 126 /// <param name="x"></param> 127 /// <param name="y"></param> 128 /// <param name="color"></param> 129 public void SetPixel(int x, int y, Color color) 130 { 131 // Get color components count 132 int cCount = Depth / 8; 133 134 // Get start index of the specified pixel 135 int i = ((y * Width) + x) * cCount; 136 137 if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha 138 { 139 Pixels[i] = color.B; 140 Pixels[i + 1] = color.G; 141 Pixels[i + 2] = color.R; 142 Pixels[i + 3] = color.A; 143 } 144 if (Depth == 24) // For 24 bpp set Red, Green and Blue 145 { 146 Pixels[i] = color.B; 147 Pixels[i + 1] = color.G; 148 Pixels[i + 2] = color.R; 149 } 150 if (Depth == 8) 151 // For 8 bpp set color value (Red, Green and Blue values are the same) 152 { 153 Pixels[i] = color.B; 154 } 155 } 156 }
测试方法
我们可以使用Benchmark类去测试LockBitmap的性能。
1 public class Benchmark 2 { 3 private static DateTime startDate = DateTime.MinValue; 4 private static DateTime endDate = DateTime.MinValue; 5 6 public static TimeSpan Span { get { return endDate.Subtract(startDate); } } 7 8 public static void Start() { startDate = DateTime.Now; } 9 10 public static void End() { endDate = DateTime.Now; } 11 12 public static double GetSeconds() 13 { 14 if (endDate == DateTime.MinValue) return 0.0; 15 else return Span.TotalSeconds; 16 } 17 }
使用方法
现在,我们可以使用LockBitmap类非常快地去操作bitmap。
1 public void ChangeColor() 2 { 3 Bitmap bmp = (Bitmap)Image.FromFile(d:\source.png); 4 Benchmark.Start(); 5 LockBitmap lockBitmap = new LockBitmap(bmp); 6 lockBitmap.LockBits(); 7 8 Color compareClr = Color.FromArgb(255, 255, 255, 255); 9 for (int y = 0; y < lockBitmap.Height; y++) 10 { 11 for (int x = 0; x < lockBitmap.Width; x++) 12 { 13 if (lockBitmap.GetPixel(x, y) == compareClr) 14 { 15 lockBitmap.SetPixel(x, y, Color.Red); 16 } 17 } 18 } 19 lockBitmap.UnlockBits(); 20 Benchmark.End(); 21 double seconds = Benchmark.GetSeconds(); 22 bmp.Save(d:\result.png); 23 }
(PS:原作者的使用样例包含了测试样例,笔者在这里给大家一个只有LockBitmap类的使用样例。)
1 string file = @"C: est.jpg"; 2 Bitmap bmp = new Bitmap(file); 3 LockBitmap lockbmp = new LockBitmap(bmp); 4 5 //锁定bitmap 6 lockbmp.lockBits(); 7 8 //通过GetPixel获取像素信息 9 Color color = lockbmp.GetPixel(0, 0); 10 11 //通过SetPixel设置像素信息 12 lockbmp.SetPixel(0, 0, Color.Black); 13 14 //解锁bitmap 15 lockbmp.UnlockBits();
摘自:https://www.codeproject.com/tips/285577/workwithbitmapsfasterincsharp#alternative3
作者:danlobo
(PS:这个帖子只是在LockBitmap的基础上继承了IDisposable接口)
总的来说,它涉及改变:
1 public class LockBitmap
变为:
1 public class LockBitmap : IDisposable
构造函数:
1 public LockBitmap(Bitmap source) 2 { 3 this.source = source; 4 }
变为:
1 public LockBitmap(Bitmap source) 2 { 3 this.source = source; 4 LockBits(); 5 }
并且新增一个函数:
1 public void Dispose() 2 { 3 UnlockBits(); 4 }
因此,这个类的使用方法,从这样:
1 LockBitmap lockBitmap = new LockBitmap(bmp); 2 lockBitmap.LockBits(); 3 (...) 4 lockBitmap.UnlockBits();
变为这样:
1 using(LockBitmap lockBitmap = new LockBitmap(bmp)) 2 { 3 (...) 4 }
我认为这样更加优雅。(PS:more elegant, IMO.)
摘自:http://www.cnblogs.com/bomo/archive/2013/02/26/2934055.html
作者:bomo
(PS:这是网上的另一种方法,但笔者更推荐第一种,也就是LockBitmap)
指针法
这种方法访问速度比内存法更快,直接通过指针对内存进行操作,不需要进行拷贝,但是在C#中直接通过指针操作内存是不安全的,所以需要在代码中加入unsafe关键字,在生成选项中把允许不安全代码勾上,才能编译通过。
这里定义成PointerBitmap类。
1 public class PointBitmap 2 { 3 Bitmap source = null; 4 IntPtr Iptr = IntPtr.Zero; 5 BitmapData bitmapData = null; 6 7 public int Depth { get; private set; } 8 public int Width { get; private set; } 9 public int Height { get; private set; } 10 11 public PointBitmap(Bitmap source) 12 { 13 this.source = source; 14 } 15 16 public void LockBits() 17 { 18 try 19 { 20 // Get width and height of bitmap 21 Width = source.Width; 22 Height = source.Height; 23 24 // get total locked pixels count 25 int PixelCount = Width * Height; 26 27 // Create rectangle to lock 28 Rectangle rect = new Rectangle(0, 0, Width, Height); 29 30 // get source bitmap pixel format size 31 Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat); 32 33 // Check if bpp (Bits Per Pixel) is 8, 24, or 32 34 if (Depth != 8 && Depth != 24 && Depth != 32) 35 { 36 throw new ArgumentException("Only 8, 24 and 32 bpp images are supported."); 37 } 38 39 // Lock bitmap and return bitmap data 40 bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite, 41 source.PixelFormat); 42 43 //得到首地址 44 unsafe 45 { 46 Iptr = bitmapData.Scan0; 47 //二维图像循环 48 49 } 50 } 51 catch (Exception ex) 52 { 53 throw ex; 54 } 55 } 56 57 public void UnlockBits() 58 { 59 try 60 { 61 source.UnlockBits(bitmapData); 62 } 63 catch (Exception ex) 64 { 65 throw ex; 66 } 67 } 68 69 public Color GetPixel(int x, int y) 70 { 71 unsafe 72 { 73 byte* ptr = (byte*)Iptr; 74 ptr = ptr + bitmapData.Stride * y; 75 ptr += Depth * x / 8; 76 Color c = Color.Empty; 77 if (Depth == 32) 78 { 79 int a = ptr[3]; 80 int r = ptr[2]; 81 int g = ptr[1]; 82 int b = ptr[0]; 83 c = Color.FromArgb(a, r, g, b); 84 } 85 else if (Depth == 24) 86 { 87 int r = ptr[2]; 88 int g = ptr[1]; 89 int b = ptr[0]; 90 c = Color.FromArgb(r, g, b); 91 } 92 else if (Depth == 8) 93 { 94 int r = ptr[0]; 95 c = Color.FromArgb(r, r, r); 96 } 97 return c; 98 } 99 } 100 101 public void SetPixel(int x, int y, Color c) 102 { 103 unsafe 104 { 105 byte* ptr = (byte*)Iptr; 106 ptr = ptr + bitmapData.Stride * y; 107 ptr += Depth * x / 8; 108 if (Depth == 32) 109 { 110 ptr[3] = c.A; 111 ptr[2] = c.R; 112 ptr[1] = c.G; 113 ptr[0] = c.B; 114 } 115 else if (Depth == 24) 116 { 117 ptr[2] = c.R; 118 ptr[1] = c.G; 119 ptr[0] = c.B; 120 } 121 else if (Depth == 8) 122 { 123 ptr[2] = c.R; 124 ptr[1] = c.G; 125 ptr[0] = c.B; 126 } 127 } 128 } 129 }
(PS:使用方法这里就不列出来了,跟上面的LockBitmap类似。)