原文:图像处理中的跨度(stride)

使用C#的BitmapData

最近要转开发平台,正研究C#。C#好是好,不过处理图片时一个像素一个像素的操作像素不是一般的慢。其实Delphi也一样,但好在Delphi的Bitmap类提供了ScanLines,可以一行一行的读图,效率比较高。C#应该也有类似的东东。经过一番搜索,终于发现了BitmapData类。

先看个例子,这是对一张位图的每个像素按FF取补,然后输出到一个新图(代码有点啰嗦,不过应该可以说明问题了)。

int h = m_Bmp.Height;

int w = m_Bmp.Width;

Bitmap bmpOut = new Bitmap(w, h, PixelFormat.Format24bppRgb);

BitmapData dataIn = m_Bmp.LockBits(new Rectangle(0,0,w,h),ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb;

BitmapData dataOut = bmpOut.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

unsafe

{

byte* pIn = (byte*)(dataIn.Scan0.ToPointer());

byte * pOut = (byte*)(dataOut.Scan0.ToPointer());

for (int y = 0; y < dataIn.Height; y++)

{

for (int x = 0; x < dataIn.Width; x++)

{

pOut[0] = (byte)(255 - pIn[0]);

pOut[1] = (byte)(255 - pIn[1]);

pOut[2] = (byte)(255 - pIn[2]);

pIn += 3;

pOut += 3;

}

pIn += dataIn.Stride - dataIn.Width * 3;

pOut += dataOut.Stride - dataOut.Width * 3;

}

}

bmpOut.UnlockBits(dataOut);

m_Bmp.UnlockBits(dataIn);

貌似比Delphi复杂得多,难道我真的天生对指针过敏?还是Delphi的比较好理解,就是扫描每一行,然后对当前像素点的三个分量做处理,非常方便。而且C#代码中的Stride是个什么东东?

查找了不少资料,现在我是这么理解的:

假设有一张图片宽度为6,因为是Format24bppRgb格式(每像素3字节。在以下的讨论中,除非特别说明,否则Bitmap都被认为是24位RGB)的,显然,每一行需要6*3=18个字节存储。对于Bitmap就是如此。但对于BitmapData,虽然BitmapData.Width还是等于Bitmap.Width,但大概是出于显示性能的考虑,每行的实际的字节数将变成大于等于它的那个离它最近的4的整倍数,此时的实际字节数就是Stride。就此例而言,18不是4的整倍数,而比18大的离18最近的4的倍数是20,所以这个BitmapData.Stride = 20。显然,当宽度本身就是4的倍数时,BitmapData.Stride = Bitmap.Width * 3。

画个图可能更好理解。R、G、B 分别代表3个原色分量字节,BGR就表示一个像素。为了看起来方便我在每个像素之间插了个空格,实际上是没有的。X表示补足4的倍数而自动插入的字节。为了符合人类的阅读习惯我分行了,其实在计算机内存中应该看成连续的一大段。

Scan0

|

|———Stride—————–|

|———Width————-| | 注:Width是图片(BGR作为一个单位)宽度

BGR BGR BGR BGR BGR BGR XX

BGR BGR BGR BGR BGR BGR XX

.

.

.

现在应该很好理解了。首先用 BitmapData.Scan0找到第0个像素的第0个分量的地址。这个地址指向的是个byte类型,所以当时定义为byte* pIn。

行扫描时,在当前指针位置(不妨看成当前像素的第0个颜色分量)连续取出三个值(3个原色分量。注意,0 1 2代表的次序是B G R。在取指针指向的值时,貌似p[n]和p += n再取p[0]是等价的),然后下移3个位置(pIn += 3,看成指到下一个像素的第0个颜色分量)。做过Bitmap.Width次操作后,就到达了Bitmap.Width * 3的位置,应该要跳过图中标记为X的字节了(共有Stride - Width * 3个字节),代码中就是 pIn += dataIn.Stride - dataIn.Width * 3;

跳过以后指针就到达下行的第0个像素了。按照此算法,一共需要做Bitmap.Height次行扫描(代码就是 for (int y = 0; y < dataIn.Height; y++))。

另外,因为使用了unsafe,所以编译的时候需要设置“允许不安全的代码”。

Posted: 2006年12月27日 14:22 作者 biojaye

图像处理中的跨度(stride)的更多相关文章

  1. 图像处理中任意核卷积(matlab中conv2函数)的快速实现。

    卷积其实是图像处理中最基本的操作,我们常见的一些算法比如:均值模糊.高斯模糊.锐化.Sobel.拉普拉斯.prewitt边缘检测等等一些和领域相关的算法,都可以通过卷积算法实现.只不过由于这些算法的卷 ...

  2. paper 119:[转]图像处理中不适定问题-图像建模与反问题处理

    图像处理中不适定问题 作者:肖亮博士 发布时间:09-10-25 图像处理中不适定问题(ill posed problem)或称为反问题(inverse Problem)的研究从20世纪末成为国际上的 ...

  3. 图像处理中的数学原理具体解释21——PCA实例与图像编码

    欢迎关注我的博客专栏"图像处理中的数学原理具体解释" 全文文件夹请见 图像处理中的数学原理具体解释(总纲) http://blog.csdn.net/baimafujinji/ar ...

  4. 图像处理中创建CDib类时无法选择基类类型时怎么办

    图像处理中创建CDib类时无法选择基类类型时怎么办? 类的类型选择Generic Class 在下面的篮筐里输入CObject就行了

  5. 图像处理中的数学原理具体解释20——主成分变换(PCA)

    欢迎关注我的博客专栏"图像处理中的数学原理具体解释" 全文文件夹请见 图像处理中的数学原理具体解释(总纲) http://blog.csdn.net/baimafujinji/ar ...

  6. 图像处理中的matlab使用

    图像的矩阵表示 类和图像类型 虽然使用的是整数坐标, 但 MATLAB 中的像素值(亮度)并未限制为整数. 表 1-1 列出了 MATLAB 和图像处理工具箱为描述像素值而支持的各种类. 表中的前 8 ...

  7. paper 109 :图像处理中的拉普拉斯算子

    1.基本理论 拉普拉斯算子是最简单的各向同性微分算子,具有旋转不变性.一个二维图像函数 的拉普拉斯变换是各向同性的二阶导数,定义为:   为了更适合于数字图像处理,将该方程表示为离散形式:    另外 ...

  8. 多线程图像处理中对选入DC的位图保护

    我在应用多线程加速图像处理(具体参见图像处理的多线程计算)的过程中,曾遇到过一个线程同步的问题.多线程对图像不同子块进行处理,再合成.结果发现最终不是全部子块都处理成功,有的子块好像没有被处理.而且发 ...

  9. SSE图像算法优化系列十七:一些图像处理中常用小过程的SSE实现。

    在做图像处理的SSE优化时,也会经常遇到一些小的过程.数值优化等代码,本文分享一些个人收藏或实现的代码片段给大家. 一.快速求对数运算 对数运算在图像处理中也是个经常会遇到的过程,特备是在一些数据压缩 ...

随机推荐

  1. python启动应用程序和终止应用程序

    python启动应用程序和终止应用程序 1. 目的 每天上班,工作需要,电脑上需要每天开机启动一些软件,下班时候,需要关掉一些软件.一个一个打开和关闭貌似是很繁琐的,于是乎,这个脚本产生了. 2. 环 ...

  2. 《⑨也懂系列:MinGW-w64安装教程》著名C/C++编译器GCC的Windows版本(MinGW-w64在安装的时候可以选择版本,有图,一步一步)

    发布日期 2016年10月31日 分类 教程 标签 编程.软件 前言<⑨也懂系列:MinGW-w64安装教程>这篇文章由 rsreland (http://rsreland.net)于 2 ...

  3. eCognition学习记录

    作者:朱金灿 来源:http://blog.csdn.net/clever101 昨天公司从外面请了人讲解eCognition的最新进展及项目二次开发应用情况.我做了大致下面记录: 1.  eCogn ...

  4. git 本地分支关联远程分支

    github上已经有master分支 和dev分支 在本地 git checkout -b dev 新建并切换到本地dev分支 git pull origin dev 本地分支与远程分支相关联 在本地 ...

  5. 将grub写入mbr

    现在习惯了开机启动的方法,所以我们把grub重新安装到硬盘的MBR上面. 这个步骤也有两种方法,一是安装grub4dos,然后启动,选中搜索硬盘上的linux引导文件,进入Linux之后再安装grub ...

  6. QT之QSignalMapper(可以理解为转发器,多个按钮绑定到一个Edit上,且能分辨。每个单独连接的话,反而麻烦)

    QT之QSignalMapper QT之QSignalMapper 简述 效果图 上代码 相关知识点文章 结尾 简述 QSignalMapper我们可以理解为转发器,此话怎讲呢?比如,按钮点击的响应槽 ...

  7. Delphi png、bmp、gif等图片格式转换成jpg

    unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...

  8. python 反转列表

    翻转一个链表 您在真实的面试中是否遇到过这个题? Yes 样例 给出一个链表1->2->3->null,这个翻转后的链表为3->2->1->null 步骤是这样的: ...

  9. cocos2d-x 打开控制面板

    于cocos2dx反过来,我们所熟悉的控制台输出,可以查看日志,例如C介面printf();性能. int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTA ...

  10. python下载图片(3)

    # -*- coding: utf-8 -*-"""some function by metaphy,2007-04-03,copyleftversion 0.2&quo ...