原文: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. windows下的定时任务设置详解

    windows下的定时任务设置详解 一.总结 一句话总结: 1.php.exe是什么? 就是php中自带的一个exe,不是我们写的,这个exe是可以执行其他的PHP的 二.windows下的定时任务设 ...

  2. Git恢复删除或修改的文件 ls-files

    一.git ls-files -d 查看删除了的文件 1.查看当前状态可以看到删除了一个index.js: git status image.png 2.查看当前项目中删除了的文件 git ls-fi ...

  3. Android自定义控件View(一)

    虽然Android API给我们提供了众多控件View来使用,但是鉴于Android的开发性,自然少不了根据需求自定义控件View了.比如说QQ头像是圆形的,但是纵观整个Android控件也找不到一个 ...

  4. js如何操作表格(常用属性方法汇总)

    js如何操作表格(常用属性方法汇总) 一.总结 一句话总结: 二.表格相关的属性和方法 1.1 Table 对象集合 cells[] 返回包含表格中所有单元格的一个数组. 语法:tableObject ...

  5. BZOJ 2064 - 状压DP

    传送门 题目大意: 给两个数组, 数组中的两个元素可以合并成两元素之和,每个元素都可以分裂成相应的大小,问从数组1变化到数组2至少需要多少步? 题目分析: 看到数据范围\(n<=10\), 显然 ...

  6. JUnit中@Test的运行顺序

    原文链接: Test execution order 原文日期: 2012年12月06日 翻译日期: 2014年10月16日 翻译人员: 百里马 依照设计,Junit不指定test方法的运行顺序. 到 ...

  7. react持续记录零散笔记

    根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff 这些生命周期在深入项目开发阶段是非常重要的 ...

  8. 聊聊QPS/TPS/并发量/系统吞吐量的概念

    原文:聊聊QPS/TPS/并发量/系统吞吐量的概念 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/cainiao_user/article/deta ...

  9. 微信公众平台开发(1) 通用的工具类CommonUtil

    1.通用的调用微信的方法 /** * * @param requestUrl 接口地址 * @param requestMethod 请求方法:POST.GET... * @param output ...

  10. WPF--3Dmax+blend+WPF综合运用

    引自:http://blog.sina.com.cn/s/blog_95dbdf9e0100we3z.html 本人小菜,WPF刚入门,只是写一下最近的项目心得.欢迎各位前辈们前来拍砖指正,感激不敬! ...