原文:C#数字图像处理时注意图像的未用区域

图1. 被锁定图像像素数组基本布局

        如图1所示,数组的宽度并不一定等于图像像素数组的宽度,还有一部分未用区域。这是为了提高效率,系统要确定每行的字节数必须为4的倍数。例如一幅24位、宽为17个像素的图像,它需要每行占有的空间为51(3
* 17)个字节,但51不是4的倍数,因此还需要扩充1个字节,从而使每行的字节数扩展为52(4 * 13,即Stride=52),这样就满足了每行字节数是4的倍数的条件。需要扩展多少个字节不仅是由图像的宽度决定,而且还由图像像素的格式决定。

        如果处理的是任意宽度的图像,那么在进行下一行扫描的时候,需要把不含图像数据、仅起对齐作用的扩展字节去掉。此时对图像像素数组的遍历的形式如下:

(1).灰度图像:

[csharp] view
plain
copy

  1. Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);  
  2. BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);  
  3. IntPtr ptr = bmpData.Scan0; // 首地址  
  4. int bytes = bmpData.Stride * bmpData.Height;    // 像素个数,包括未用空间  
  5. byte[] grayValues = new byte[bytes];  
  6. System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);  
  7. for (int i = 0; i < bmpData.Height; i++)  
  8. {  
  9.         // 仅处理每行中为图像像素的数据,舍弃未用空间  
  10.         for (int j = 0; j < bmpData.Width; j++)  
  11.         {  
  12.                 // use of grayValues[i * bmpData.Stride + j];  
  13.         }  
  14. }  
  15. System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr, bytes);  
  16. bitmap.UnlockBits(bmpData);  

或者:

[csharp] view
plain
copy

  1. // 获取图像参数  
  2. int stride = bmpData.Stride;            // 扫描线的宽度  
  3. int offset = stride - width;            // 显示宽度与扫描线宽度的间隙  
  4. IntPtr iptr = bmpData.Scan0;            // 获取bmpData的内存起始位置  
  5. int scanBytes = stride * height;        // 用stride宽度,表示这是内存区域的大小  
  6.   
  7. // 位置指针,指向源数组  
  8. int posScan = 0;  
  9. byte[] grayValues = new byte[scanBytes];    // <span style="font-family: Arial; ">为目标数组分配内存</span>  
  10.   
  11. for (int x = 0; x < height; x++)  
  12. {  
  13.         // 下面的循环节是模拟行扫描  
  14.         for (int y = 0; y < width; y++)  
  15.         {  
  16.                 // grayValues[posScan++]  
  17.         }  
  18.         posScan += offset;               // 行扫描结束,跳过未用空间字节  
  19. }  

(2).24位RGB图像:

[csharp] view
plain
copy

  1. Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);  
  2. BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);  
  3. IntPtr ptr = bmpData.Scan0; // 首地址  
  4. int bytes = bmpData.Stride * bmpData.Height;    // 像素个数,包括未用空间  
  5. byte[] rgbValues = new byte[bytes];  
  6. System.Runtime.InteropServices.Marshal.Copy(ptr, rbgValues, 0, bytes);  
  7. for (int i = 0; i < bmpData.Height; i++)  
  8. {  
  9.         // 仅处理每行中为图像像素的数据,舍弃未用空间  
  10.         for (int j = 0; j < bmpData.Width * 3; j += 3)  
  11.         {  
  12.                 // R: rgbValues[i * bmpData.Stride + j + 2]  
  13.                 // G: rgbValues[i * bmpData.Stride + j + 1]  
  14.                 // B: rgbValues[i * bmpData.Stride + j]  
  15.         }  
  16. }  
  17. System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);  
  18. bitmap.UnlockBits(bmpData);  

        以上代码将图像的整个像素数组都复制到数组grayValue或者rgbValue中,包括像素和仅用于对齐的未用空间,然后再在数组grayValue或者rgbValue中只对像素数据进行处理,跳过每一行的未用空间(跳过末尾几列)。

        以下实验中,对任意尺度(每行字节数不是4字节的整数倍)的图像进行灰度直方图绘制,如果不考虑未用空间的话会导致错误结果(本实验中错误程度较小)。

图2.未考虑图像中未用空间的错误结果

        用Matlab进行灰度直方图计算会发现真实结果中最大灰度的像素个数不是52811,而是53195。

  1. f = imread('GrayTest.bmp');  
  2. h = imhist(f)  
图3.Matlab计算出的最大频率灰度像素个数

        

        错误的原因是,因为图像的字节宽度不是4字节的整数倍,图像的像素数组包括了未用空间(每一行中都包括了未用空间)程序只从规模为bmpData.Stride * bmpData.Height字节的像素数组中复制了curBitmap.Width * curBitmap.Height规模的字节(灰度像素,1字节),这些字节中既包含了像素也包含了未用空间,如图4所示:

图4.出错时对像素数组复制的字节

        最后在代码中考虑未用空间能得到正确结果:

图5.正确结果

C#数字图像处理时注意图像的未用区域的更多相关文章

  1. Win8 Metro(C#)数字图像处理--3.2图像方差计算

    原文:Win8 Metro(C#)数字图像处理--3.2图像方差计算 /// <summary> /// /// </summary>Variance computing. / ...

  2. Win8 Metro(C#)数字图像处理--3.3图像直方图计算

    原文:Win8 Metro(C#)数字图像处理--3.3图像直方图计算 /// <summary> /// Get the array of histrgram. /// </sum ...

  3. Win8 Metro(C#)数字图像处理--3.4图像信息熵计算

    原文:Win8 Metro(C#)数字图像处理--3.4图像信息熵计算 [函数代码] /// <summary> /// Entropy of one image. /// </su ...

  4. Win8 Metro(C#)数字图像处理--3.5图像形心计算

    原文:Win8 Metro(C#)数字图像处理--3.5图像形心计算 /// <summary> /// Get the center of the object in an image. ...

  5. Win8 Metro(C#)数字图像处理--3.1图像均值计算

    原文:Win8 Metro(C#)数字图像处理--3.1图像均值计算 /// <summary> /// Mean value computing. /// </summary> ...

  6. Win8 Metro(C#)数字图像处理--2.74图像凸包计算

    原文:Win8 Metro(C#)数字图像处理--2.74图像凸包计算 /// <summary> /// Convex Hull compute. /// </summary> ...

  7. Win8 Metro(C#)数字图像处理--2.68图像最小值滤波器

    原文:Win8 Metro(C#)数字图像处理--2.68图像最小值滤波器 /// <summary> /// Min value filter. /// </summary> ...

  8. Win8 Metro(C#)数字图像处理--2.52图像K均值聚类

    原文:Win8 Metro(C#)数字图像处理--2.52图像K均值聚类  [函数名称]   图像KMeans聚类      KMeansCluster(WriteableBitmap src,i ...

  9. Win8 Metro(C#)数字图像处理--2.45图像雾化效果算法

    原文:Win8 Metro(C#)数字图像处理--2.45图像雾化效果算法 [函数名称]   图像雾化         AtomizationProcess(WriteableBitmap src,i ...

随机推荐

  1. iOS Animation具体解释

    iOS Animation具体解释 本篇仅仅要解说iOS中动画的使用. Animtion主要分为两类:UIView动画和CoreAnimation动画. UIView动画有UIView属性动画,UIV ...

  2. [Postgres] Filter Data in a Postgres Table with Query Statements

    We have all this data, but how do we answer questions about it? In this lesson we’ll learn how to fi ...

  3. Spirng setter 注入简单

    1.      提供相应要注入的属性 //setter注入 public class Bean2 { private String name; private Integer age; // 提供要注 ...

  4. JAVA 安装JDK注意事项

    在JAVA jdk安装的时候,文件夹名称最好不要有中文,有逗号,有下划线,空格等问题.这样easy出错,比方以下几个安装路径easy出错    java文件夹/jdk1.6_x86/    这样的就是 ...

  5. 【codeforces 779B】Weird Rounding

    [题目链接]:http://codeforces.com/contest/779/problem/B [题意] 问你要删掉几个数字才能让原来的数字能够被10^k整除; [题解] /* 数字的长度不大; ...

  6. boost-1.53源代码编译

    1.下载源代码.首先得明确,boost大多数库都仅仅有头文件.链接时不需Lib文件:下面库须要单独编译成库: Boost.Filesystem Boost.GraphParallel Boost.IO ...

  7. XCode5.1.1怎样实现代码块自己主动排版

    1.问题描写叙述 在开发中常常遇到代码块须要排版的情况,假设一行一行缩进就太out了,但在xcode中怎样实现还真不知道 2.解决方法 2.1选中须要排版的代码块 2.2单击"Editor ...

  8. Sleep(0)的妙用

    在线程中,调用sleep(0)可以释放cpu时间,让线程马上重新回到就绪队列而非等待队列,sleep(0)释放当前线程所剩余的时间片(如果有剩余的话),这样可以让操作系统切换其他线程来执行,提升效率. ...

  9. Android 平台下Ftp 使用模拟器需要注意的问题

    以下代码在pc上测试通过,可是在android模拟器上就不工作,不过还可以链接到服务器但不能得到文件 纠结了半天,原来是模式的问题,具体请Google 模拟器中采用建议被动模式 public void ...

  10. hudson搭建经验总结

    作者:朱金灿 来源:http://blog.csdn.net/clever101 hudson 是一种革命性的开放源码 CI (持续集成)服务器,随着工程源码越来越庞大,把源码编译工作放在本地机器已不 ...