在unsafe下灰度图象处理方法的完结版本!

           这些日子,花了很大工夫来学习GDI+,理解了不少东西!下面这篇文章是我对我在论坛上发表的文章“unsafe下图象增亮处理算法怎么解释?”的整理版本!很全的!希望对大家有所帮助!

           在.NET环境下用过GDI+的朋友可能都有种感觉,速度很慢!这在我处理图象的灰度时很明显,于是我问朋友,问同学,他们告诉我在.NET环境下使用指针能解决这个问题,并给我下面这个实现增加图象亮度效果的代码:

 // GDI+ return format is BGR, NOT RGB.

Bitmap b= new Bitmap(pictureBox1.Image);
BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
int nVal;
byte * p = (byte *)(void *)Scan0;
int nOffset = stride - b.Width*3;
int nWidth = b.Width * 3;
for(int y=0;y<b.Height;++y)
{
for (int x = 0;  x < nWidth; ++x)
{
nVal = (int) (p[0] + brightness);
if (nVal < 0) nVal = 0;
if (nVal > 255) nVal = 255;
p[0] = (byte)nVal;
++p;
}
p += nOffset;
}
}
b.UnlockBits(bmData);
pictureBox2.Image=b; 

我尝试着利用这个代码处理了图象,确实很快,但是我不理解这个代码,下面的内容是我对这段代码的理解过程!

 先说下图象扫描的过程:在将光信号转换为电信号的扫描过程中,扫描总是从图象的左上角开始,水平向前行进,同时扫描点也已较慢的速度向下移动。当扫描点移动到右侧边缘时,扫描点快速返回左侧,重新开始第一行的起点下面进行第二行扫描,行与行之间的返回过程成为水平消隐。一幅完整的图象扫描信号,由水平消隐间隔分开的行信号序列构成,称为一帧。
   再说下BitmapData的属性!
   Heigh:表示像素高度
   PiexlFormat:使用PiexlFormat枚举表示的像素格式
   Scan0:表示位图中第一个像素数据的地址
   Stride:表示步幅(也称为扫描宽度)
   Width:表示像素宽度
   好了,现在来看问题:
首先利用bitmap对象的LockBits()方法将整幅图象像素锁定到内存待编辑!这里注意PixelFormat.Format24bppRgb,它表示欲编辑的这幅图象每个像素24位,红色、绿色和蓝色分量分别使用8位。这就是说一个像素点的颜色信息用3byte来表示!这很关键!接下来的代码是获取扫描宽度,我们看上面所说的扫描过程,“当扫描点移动到右侧边缘时,扫描点快速返回左侧”,我想这种机理是扫描在到一行的末尾像素,它会继续向前扫描一个像素位置,然后根据扫描此点像素的结果做某种判断,结果就是已经扫描完成了该行,要立刻返回下一行的行首了!如果不这样理解,int nOffset = stride - b.Width*3将解释不通!程序接下来是获取或者设置位图中第一个像素数据的地址,它可以看成是位图中第一个扫描行,这里提下扫描一行像素,它记录的上该点像素的三个颜色分量的信息。好了,有了上面的理解,我们再来看unsafe包括起来的部分!它总的想法是获取某点像素信息的首地址,然后更改此像素的各颜色分量的值,它对应代码如下:
nVal = (int) (p[0] + brightness);
if (nVal < 0) nVal = 0;
if (nVal > 255) nVal = 255;
p[0] = (byte)nVal;
++p;
然后我要说的是:代码p += nOffset;注意这个代码是出现在对一行像素所对应的颜色信息更改后才出现的,结合我上面的那个叙述,概括是这个意思:扫描完一行像素后再扫描一个像素的信息,再回头!所以要把这个多扫描的像素颜色信息跳过!应该就是这样!
程序的执行应该就是这样,待对所有像素的信息进行了调整后,代码执行了一个将整幅图象像素从内存中解除锁定,并将变换后的图象赋给图象控件!对应代码如下:
b.UnlockBits(bmData);
pictureBox2.Image=b; 
这是我查询不少资料的理解,这样理解后,确实问题理解上去不难了!但是总感觉不妥当,不妥当在于我自己想象的那段扫描过程!因为缺少理论根据!于是,我又到处找,终于在一个国外网站找到这个问题的解释,它有一副图,如下,详细的说明了stride和scan0,以及rows的关系!

该文章还给了段文字解释:The Stride property, as shown in figure 1, holds the width of one row in bytes. The size of a row however may not be an exact multiple of the pixel size because for efficiency, the system ensures that the data is packed into rows that begin on a four byte boundary and are padded out to a multiple of four bytes. This means for example that a 24 bit per pixel image 17 pixels wide would have a stride of 52. The used data in each row would take up 3*17 = 51 bytes and the padding of 1 byte would expand each row to 52 bytes or 13*4 bytes.
它的意思是说,一行stride除了包括像素的信息外,还有个padding,它占的空间很少,为了更加形象的说明问题,它举了例:说24bit/per的图象一行有17个像素,但这行的宽度是:17*3+1=52!这个1就是padding的宽度!
好了,加上我的理解和这幅图的帮助,整个程序就弄明白了!好开心!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值