【转】使用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的整数倍。
具体数据的分布因其pixel format而异。24位深的图像每隔3个byte包含一组RGB信息;32位深的图像每隔4个byte包含一组RGBA信息。那些每个字节包含多个像素的pixel format,比如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);
byte currentByte = ((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();
int index=y*bmd.Stride+(x>>3);
byte mask=(byte)(0x80>>(x&0x7));
if(pixel)
p[index]|=mask;
else
p[index]&=(byte)(mask^0xff);
最后在进行完所有处理后马不要忘记使用Unlockbits命令解锁。
原文在这里http://blog.sina.com.cn/s/blog_4e3e2ce4010009on.html
【转】使用lockbits方法处理图像的更多相关文章
- C#中使用lockbits方法处理图像
转自 使用lockbits方法处理图像(转) 许多图像处理任务即时是最简单的文件类型转换,例如从32位深度到8位深度的格式转化,直接获得像素阵列要比使用GetPixel和SetPixel等方法的效率高 ...
- 类库探源——System.Drawing.Bitmap
一.System.Drawing.Bitmap Bitmap 类: 封装GDI+ 位图,此位图由图形图像及其属性的像素数据组成.Bitmap 是用于处理由像素定义的图像的对象 命名空间: System ...
- C#中的bitmap类和图像像素值获取方法
一.Bitmap类 Bitmap对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组成.因此Bitmap是用于处理由像素数据定义的图像的对象.该类的主要方法和属性如下: 1. GetP ...
- c#图像处理入门(-bitmap类和图像像素值获取方法) 转
一.Bitmap类 Bitmap对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组成.因此Bitmap是用于处理由像素数据定义的图像的对象.该类的主要方法和属性如下: 1. GetP ...
- c#图像处理入门(-bitmap类和图像像素值获取方法)
c#图像处理入门 -bitmap类和图像像素值获取方法 一.Bitmap类 Bitmap对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组成.因此Bitmap是用于处理由像素数据定义 ...
- OpenCV ——遍历图像方法
转自http://blog.csdn.net/daoqinglin/article/details/23628125 ; y < testImage->height; y++) { uch ...
- [Swift通天遁地]五、高级扩展-(8)ImageView(图像视图)的各种扩展方法
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- C#数字图像处理的3种方法
本文主要通过彩色图象灰度化来介绍C#处理数字图像的3种方法,Bitmap类.BitmapData类和Graphics类是C#处理图像的的3个重要的类. Bitmap只要用于处理由像素数据定义的图像的对 ...
- [C#编程参考]把图像转换为数组的两种实现
当一个程序和一个图片放在一起,无非有两种操作: 第一种,就是传输这个图片,在传输图片之前要首先把这个图片变成byte类型的数组.所以这时候我们用到的是图片的存储的数据,也就是图片属性中的大小.我们并不 ...
随机推荐
- youku视频
获取视频信息: http://v.youku.com/player/getPlayList/VideoIDS/153548356 <div class="player" id ...
- ROC 曲线简要解释
阳性 (P, positive)阴性 (N, Negative)真阳性 (TP, true positive):正确的肯定.又称:命中 (hit)真阴性 (TN, true negative):正确的 ...
- 面试题一:linux面试题
2.4 写出一种排序算法(原理),并说出优化它的方法. 2.5 请简单阐述您最得意的开发之作 2.6 对于大流量的网站,您采用什么样的方法来解决各页面访问量统计问题 a. 确认服务器是否能支撑当前访问 ...
- ios支付宝问题整合
1. 报错:rsa_private read error : private key is NULL 原因:私钥没有转成PKCS8 解决方法: 1)在RSADataSigne ...
- HTML5学习笔记(十四):变量作用域
在JavaScript中,用var申明的变量实际上是有作用域的. 如果一个变量在函数体内部申明,则该变量的作用域为整个函数体,在函数体外不可引用该变量: function foo() { var x ...
- Android 自定义Dialog 去除阴影
自定义Dialog中添加下列代码: window.clearFlags( WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- linux Ctrl+z和Ctrl+c的区别
1.Ctrl+z 挂起进程,并不会结束,执行fg命令可以重新启动这个被挂起的命令. 2.Ctrl+c 终止进程
- java只使用try和finally不使用catch的原因和场景
JDK并发工具包中,很多异常处理都使用了如下的结构,如AbstractExecutorService,即只有try和finally没有catch. class X { private final Re ...
- yum卸载失败Error in PREUN scriptlet in rpm package postgresql-server
yum --setopt=tsflags=noscripts remove 参考 https://serverfault.com/questions/613256/yum-error-in-preun ...
- System.in的用法
方法1 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));Scanner scanner=new Sca ...