Bitmap的优化

摘自:http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp#_rating

作者:Vano Maisuradze

  当你在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 }

  我认为这样更加优雅。(PSmore 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类似。)

原文地址:https://www.cnblogs.com/Bita/p/6351482.html