使用lockbits方法处理图像


   许多图像处理任务即时是最简单的文件类型转换,例如从32位深度到8位深度的格式转化,直接获得像素阵列要比使用GetPixel和SetPixel等方法的效率高得多。
        你可能会发现DotNet采用托管机制,大多数情况下微软会推荐你使用托管代码,理由是便捷和安全。实际应用中,直接操作内存中的数据块是很少见的,尽管如此,图像处理恰恰是这类为数不多的情况之一,因为使用托管代码的效率低的难以忍受,特别是对巨幅图像来说,在此,我们讨论一下一种新的方法。
        如何使用非托管代码是因语言而异的,在C#中我们可以通过unsafe关键字来调用指针,从而直接操作内存中的位图数据;VB则使用Marshal类中的方法,它会导致一部分的性能损失,因此效率不如前者。
 
锁定比特流

    Bitmap类使用LockBits和UnLockBits方法来将位图的数据矩阵保存在内存中、直接对它进行操作,最后用修改后的数据代替位图中的原始数据。 LockBits返回以各BitmapData的类用已描述数据在已锁定的矩阵中的位置和分布。
      BitmapData类包括以下几个重要的属性:

Scan0:数据矩阵在内存中的地址。

  • Stride:数据矩阵中的行宽,以byte为单位。可能会扩展几个Byte,后面会介绍。

  • PixelFormat:像素格式,这对矩阵中字节的定位很重要。

  • Width:位图的宽度。

  • Height:位图的高度。

      具体关系见下图:


     
       如上图所示,stride属性表示位图数据矩阵的行宽,以byte为单位。出于效率考虑,矩阵的行宽并非刚好是每行像素数的整数倍,系统往往会将其封装成4的整数倍。举例来说,对于一幅24位深17像素宽的图像,其stride属性为52;每行的数据量为17*3=51,系统将其自动封装一个字节,所以它的stride为52byte(或13*4byte)。对于一幅17像素宽的4位索引图,其stride为12,其中9byte(准确地说是8.5个byte)用来记录数据信息,每行再自动添加3(3.5)个byte保证其为4的整数倍。
       具体数据的分布因其pixelformat而异。24位深的图像每隔3个byte包含一组RGB信息;32位深的图像每隔4个byte包含一组RGBA信息。那些每个字节包含多个像素的pixelformat,比如4位索引图像或1位索引图像,必须经过仔细处理,从而保证同一字节中的相邻byte不会混淆。
    指针的准确定位

    32位RGB:假设X、Y为位图中像素的坐标,则其在内存中的地址为scan0+Y*stride+X*4。此时指针指向蓝色,其后分别是绿色、红色,alpha分量。

  • 24位RGB:scan0+Y*stride+X*3。此时指针指向蓝色,其后分别是绿色和红色。

  • 8位索引:scan0+Y*stride+X。当前指针指向图像的调色盘。

  • 4位索引:scan0+Y*stride+(X/2)。当前指针所指的字节包括两个像素,通过高位和低位索引16色调色盘,其中高位表示左边的像素,低位表示右边的像素。

  • 1位索引:scan0+Y*stride+X/8。当前指针所指的字节中的每一位都表示一个像素的索引颜色,调色盘为两色,最左边的像素为8,最右边的像素为0 。

    像素间使用迭代器

       下面这个范例将一幅32位深的图像中所有像素的蓝色分量设为最大(255):

         

         

    [csharp] view plain copy
    print ?
    1. BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10),     
    2.            System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);  
    3.   
    4.       int PixelSize=4;  
    5.   
    6.    
    7.   
    8.       for(int y=0; y<bmd.Height; y++)  
    9.   
    10.       {  
    11.   
    12.         byte* row=(byte *)bmd.Scan0+(y*bmd.Stride);  
    13.   
    14.         for(int x=0; x<bmd.Width; x++)  
    15.   
    16.         {  
    17.   
    18.           row[x*PixelSize]=255;  
    19.   
    20.         }  
    21.   
    22.       }  
    BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10),   
               System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);
    
          int PixelSize=4;
    
    
    
          for(int y=0; y<bmd.Height; y++)
    
          {
    
            byte* row=(byte *)bmd.Scan0+(y*bmd.Stride);
    
            for(int x=0; x<bmd.Width; x++)
    
            {
    
              row[x*PixelSize]=255;
    
            }
    
          }



         处理4位索引图,高低位应分开处理,代码如下:

       

    [csharp] view plain copy
    print ?
    1. int offset = (y * bmd.Stride) + (x >> 1);  
    2.   
    3.    bytecurrentByte = ((byte *)bmd.Scan0)[offset];  
    4.   
    5.    if((x&1)== 1)  
    6.   
    7.    {  
    8.   
    9.      currentByte&= 0xF0;  
    10.   
    11.      currentByte|= (byte)(colorIndex & 0x0F);  
    12.   
    13.    }  
    14.   
    15.    else  
    16.   
    17.    {  
    18.   
    19.      currentByte&= 0x0F;  
    20.   
    21.      currentByte|= (byte)(colorIndex << 4);  
    22.   
    23.    }  
    24.   
    25.    ((byte*)bmd.Scan0)[offset]=currentByte;  
      int offset = (y * bmd.Stride) + (x >> 1);
    
         bytecurrentByte = ((byte *)bmd.Scan0)[offset];
    
         if((x&1)== 1)
    
         {
    
           currentByte&= 0xF0;
    
           currentByte|= (byte)(colorIndex & 0x0F);
    
         }
    
         else
    
         {
    
           currentByte&= 0x0F;
    
           currentByte|= (byte)(colorIndex << 4);
    
         }
    
         ((byte*)bmd.Scan0)[offset]=currentByte;

     

         处理1位索引的代码:

         

    [csharp] view plain copy
    print ?
    1. byte* p=(byte*)bmd.Scan0.ToPointer();  
    2.   
    3.      intindex=y*bmd.Stride+(x>>3);  
    4.   
    5.      bytemask=(byte)(0x80>>(x&0x7));  
    6.   
    7.      if(pixel)  
    8.   
    9.        p[index]|=mask;  
    10.   
    11.      else  
    12.   
    13.        p[index]&=(byte)(mask^0xff);  
    byte* p=(byte*)bmd.Scan0.ToPointer();
    
         intindex=y*bmd.Stride+(x>>3);
    
         bytemask=(byte)(0x80>>(x&0x7));
    
         if(pixel)
    
           p[index]|=mask;
    
         else
    
           p[index]&=(byte)(mask^0xff);

     

          最后在进行完所有处理后马不要忘记使用Unlockbits命令解锁。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值