zoukankan      html  css  js  c++  java
  • 使用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):

         

         

    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位索引图,高低位应分开处理,代码如下:

       

      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位索引的代码:

         

    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命令解锁。

  • 相关阅读:
    我爱Java系列之---【SpringBoot打成war包部署】
    279. Perfect Squares
    矩阵dfs--走回路
    112. Path Sum
    542. 01 Matrix
    106. Construct Binary Tree from Inorder and Postorder Traversal
    105. Construct Binary Tree from Preorder and Inorder Traversal
    Invert Binary Tree
    563 Binary Tree Tilt
    145 Binary Tree Postorder Traversal
  • 原文地址:https://www.cnblogs.com/hanwest/p/2881882.html
Copyright © 2011-2022 走看看