zoukankan      html  css  js  c++  java
  • 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类似。)

  • 相关阅读:
    gerrit权限控制
    kvm虚拟机根目录磁盘扩容
    vim新手指南
    精通 vim 你应该理解的几个名词
    精通 VIM ,此文就够了
    linux下库的使用--动态库
    linux下库的使用--静态库
    linux下程序编译的各个阶段记录
    ASCII码表
    重构的过程记录--之利用系统数据库:
  • 原文地址:https://www.cnblogs.com/Bita/p/6351482.html
Copyright © 2011-2022 走看看