使用lockbits方法处理图像(转)

摘要:
锁定位流Bitmap类使用LockBits和UnLockBits方法将位图数据矩阵存储在内存中,直接对其进行操作,最后用修改后的数据替换位图中的原始数据。LockBits基于BitmapData类返回所述数据在锁定矩阵中的位置和分布。特定数据的分布因其像素格式而异。24位深度图像每三个字节包含一组RGB信息;32位深度图像每四个字节包含一组RGBA信息。4位索引:scan0+Y*街道+(X/2)。

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

    Bitmap类使用LockBits和UnLockBits方法来将位图的数据矩阵保存在内存中、直接对它进行操作,最后用修改后的数据代替位图中的原始数据。LockBits返回以各BitmapData的类用已描述数据在已锁定的矩阵中的位置和分布。
   BitmapData类包括以下几个重要的属性:
  • Scan0:数据矩阵在内存中的地址。
  • Stride:数据矩阵中的行宽,以byte为单位。可能会扩展几个Byte,后面会介绍。
  • PixelFormat:像素格式,这对矩阵中字节的定位很重要。
  • Width:位图的宽度。
  • Height:位图的高度。

  具体关系见下图:

使用lockbits方法处理图像(转)第1张

 
   如上图所示,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命令解锁。

免责声明:文章转载自《使用lockbits方法处理图像(转)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇centos 怎么安装 g++zabbix Server 4.0 触发器(Trigger)篇下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

矩阵LU分解

    有如下方程组 ,当矩阵 A 各列向量互不相关时, 方程组有位移解,可以使用消元法求解,具体如下:     使用消元矩阵将 A 变成上三角矩阵 ,     ,     使用消元矩阵作用于向量 b,得到向量 c,,     ,     Ax=b 消元后变为 ,即 , 由于  为上三角矩阵, 使用回带法即可求解方程组。     对矩阵  做如下运算 。在...

高动态范围光照(High Dynamic Range Imaging,简称HDRI或HDR)

1 HDR基本概念 高动态范围光照(High Dynamic Range Imaging,简称HDRI或HDR),是一种表达超过了显示器所能表现的亮度范围的图像映射技术,已成为目前游戏应用不可或缺的一部分。通常,显示器能够显示R、G、B分量在[0, 255]之间的像素值。而256个不同的亮度级别显然不能表示自然界中光线的亮度情况。比如,太阳的亮度可能是一个...

c++ 动态判断基类指针指向的子类类型(typeid)

我们在程序中定义了一个基类,该基类有n个子类,为了方便,我们经常定义一个基类的指针数组,数组中的每一项指向都指向一个子类,那么在程序中我们如何判断这些基类指针是指向哪个子类呢? 本文提供了两种方法 (1) 自定义类id, (2)typeid 一、自定义id 如下所示基类father有两个子类son1 和 son2,我们在基类中定义类虚函数id,子类中分别重...

超详细介绍 图像处理(卷积)(转)

图像处理(卷积)作者太棒了    原文   http://blog.sina.com.cn/s/blog_4bdb170b01019atv.html 图像处理-线性滤波-1 基础(相关算子、卷积算子、边缘效应) 这里讨论利用输入图像中像素的小邻域来产生输出图像的方法,在信号处理中这种方法称为滤波(filtering)。其中,最常用的是线性滤波:输出...

OpenCV-C++ Mat对象的使用

这一篇文章中主要介绍有关Mat类的使用,主要包括: 通过构造函数定义Mat对象; Mat对象常用属性以及方法 创建一个空白图像并进行赋值 创建一个小数组 通过构造函数定义Mat对象 Mat中提供了很多的构造函数,这里使用两种方式: Mat(Size size, int type); Mat(int rows, int cols, int type, c...

C++之迭代器

迭代器的概念 迭代器是用来访问string对象或vector对象的元素的,类似于下标运算和指针。 其对象是容器中的元素或string对象中的字符; 使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素。 迭代器的使用 不同于指针,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员; 比如,这些类型拥有名为begin和end的成...