这是个简单的算法,是全局二值算法的一种,算法执行速度快。

算法过程简单描述如下: 

  对于每一个像素,做如下处理 

  1、计算当前像素水平和垂直方向的梯度。 (two gradients are calculated  |I(x + 1, y) - I(x - 1, y)| and |I(x, y + 1) - I(x, y - 1)|);

       2、取两个梯度的最大值作为权重。(weight is calculated as maximum of two gradients);

  3、更新权重的和。(sum of weights is updated (weightTotal += weight));

       4、更新加权像素之和 (sum of weighted pixel values is updated (total += weight * I(x, y)));

之后,最终的阈值去加权像素之和和权重之和相除的值。

这个算法在 Image Processing Lab in c# 的代码中有相关的说明。

从实际的操作上讲,我认为二值处理应该只针对灰度图像进行处理,这样才意义明确,因此,我在代码中给出了判断一副图像是否是灰度图像的一个函数:

  1.   private bool IsGrayBitmap(Bitmap Bmp)
  2. {
  3. bool IsGray;
  4. if (Bmp.PixelFormat == PixelFormat.Format8bppIndexed) // .net中灰度首先必然是索引图像
  5. {
  6. IsGray = true;
  7. if (Bmp.Palette.Entries.Length != ) // 这个要求其实在PS中是不存在的
  8. IsGray = false;
  9. else
  10. {
  11. for (int X = ; X < Bmp.Palette.Entries.Length; X++) // 看看调色板的每一个分两值是不是都相等,且必须还要等于其在调色板中出现的顺序
  12. {
  13. if (Bmp.Palette.Entries[X].R != X || Bmp.Palette.Entries[X].G != X || Bmp.Palette.Entries[X].B != X)
  14. {
  15. IsGray = false;
  16. break;
  17. }
  18. }
  19. }
  20. }
  21. else
  22. {
  23. IsGray = false;
  24. }
  25. return IsGray;
  26. }

  实际上,在PS的概念中,灰度图像的调色板个数不一定是256,只要调色板的每个元素的分量值都相等,并且都等于其在调色板中出现的顺序,PS就认为他是灰度图像。

为了处理方便,我加入了一个将其他模式的图像转换为灰度模式图像的函数:

  1. private Bitmap ConvertToGrayModeBitmap(Bitmap Bmp)
  2. {
  3. int X, Y, SrcStride, DestStride, Width, Height;
  4. byte* SrcData, DestData;
  5. BitmapData BmpData = Bmp.LockBits(new Rectangle(, , Bmp.Width, Bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
  6. Bitmap GrayBmp = new Bitmap(Bmp.Width, Bmp.Height, PixelFormat.Format8bppIndexed);
  7. ColorPalette Pal = GrayBmp.Palette;
  8. for (Y = ; Y < Pal.Entries.Length; Y++) Pal.Entries[Y] = Color.FromArgb(, Y, Y, Y); // 设置灰度图像的调色板
  9. GrayBmp.Palette = Pal;
  10.  
  11. // LockBits 在第一个参数和图像一样大,以及读取格式和原始一样的情况下,调用函数的时间为0,且每次调用后BitmapData的Scan0都相同,而在
  12. // 其他的大部分情况下同样参数调用该函数返回的Scan0都不同,这就说明在在程序内部,GDI+为在创建图像时还是分配了和对应位图一样大小内存空间,
  13. // 这样我们就可以再加载时调用一次该函数,并记住Scan0的值,然后直接用指针操作这一片区域,就相当于操作了图像。而不用每次都LOCK和UNLOCK了
  14. // 从这个层次上说,该函数和GetDibits类似。
  15.  
  16. BitmapData GrayBmpData = GrayBmp.LockBits(new Rectangle(, , GrayBmp.Width, GrayBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
  17. Width = BmpData.Width; Height = BmpData.Height;
  18. SrcStride = BmpData.Stride; DestStride = GrayBmpData.Stride; // 这个值并不一定就等于width*height*色深/8
  19. for (Y = ; Y < Height; Y++)
  20. {
  21. SrcData = (byte*)BmpData.Scan0 + Y * SrcStride; // 必须在某个地方开启unsafe功能,其实C#中的unsafe很safe,搞的好吓人。
  22. DestData = (byte*)GrayBmpData.Scan0 + Y * DestStride;
  23. for (X = ; X < Width; X++)
  24. {
  25. *DestData = (byte)((*SrcData * + *(SrcData + ) * + *(SrcData + ) * ) >> ); //这里可以有不同的算法
  26. SrcData += ;
  27. DestData++;
  28. }
  29. }
  30. Bmp.UnlockBits(BmpData);
  31. GrayBmp.UnlockBits(GrayBmpData);
  32. return GrayBmp;
  33. }

在很多人心目中所谓的灰度图像就是R=G=B这样的图像,只能说这些人还是门外汉,太不专业了。 这样的图像只能算是颜色分量相同的彩色图像罢了,再次予以纠正。

由于上述所描述的算法涉及到了图像的四领域,因此我们采用类似PhotoShop算法原理解析系列 - 风格化---》查找边缘 一文中的哨兵算法,对备份的图像扩充边界,扩充部分的数据以原始图像边界处的值填充。因为只涉及到了四领域,因此需要在图像宽度和高度上分别增加2个像素即可。

关于填充数据,我还是喜欢自己分配内存,而且我更倾向于直接使用API,这个可能与个人习惯有关吧,你们也可以按照自己的方式来处理。

  1. private byte GetSimpleStatisticsThreshold(Bitmap GrayBmp)
  2. {
  3. int Width, Height, Stride, X, Y;
  4. int CloneStride, Ex, Ey;
  5. int Weight = ;
  6. long SumWeight = ; // 对于大图像这个数字会溢出,所以用long类型的变量
  7. byte* Pointer, Scan0, CloneData;
  8.  
  9. BitmapData GrayBmpData = GrayBmp.LockBits(new Rectangle(, , GrayBmp.Width, GrayBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
  10.  
  11. Width = GrayBmp.Width; Height = GrayBmp.Height; Stride = GrayBmpData.Stride; CloneStride = Width + ; Scan0 = (byte*)GrayBmpData.Scan0;
  12. CloneData = (byte*)GlobalAlloc(GPTR, CloneStride * (Height * ));
  13.  
  14. for (Y = ; Y < Height; Y++)
  15. {
  16. *(CloneData + (Y + ) * CloneStride) = *(Scan0 + Y * Stride); // 填充左侧第一列像素(不包括第一个和最后一个点)
  17. CopyMemory(CloneData + CloneStride * (Y + ) + , Scan0 + Y * Stride, Width);
  18. *(CloneData + (Y + ) * CloneStride + Width + ) = *(Scan0 + Y * Stride + Width - ); // 填充最右侧那一列的数据
  19. }
  20. CopyMemory(CloneData, CloneData + CloneStride, CloneStride); // 第一行
  21. CopyMemory(CloneData + (Height + ) * CloneStride, CloneData + Height * CloneStride, CloneStride); // 最后一行
  22.  
  23. for (Y = ; Y < Height; Y++)
  24. {
  25. Pointer = CloneData + (Y + ) * CloneStride + ;
  26. for (X = ; X < Width; X++)
  27. {
  28. Ex = *(Pointer - ) - *(Pointer + );
  29. if (Ex < ) Ex = -Ex;
  30. Ey = *(Pointer - CloneStride) - *(Pointer + CloneStride);
  31. if (Ey < ) Ey = -Ey;
  32. if (Ex > Ey)
  33. {
  34. Weight += Ex;
  35. SumWeight += *Pointer * Ex;
  36. }
  37. else
  38. {
  39. Weight += Ey;
  40. SumWeight += *Pointer * Ey;
  41. }
  42. Pointer++;
  43. }
  44. }
  45. GlobalFree((IntPtr)CloneData);
  46. GrayBmp.UnlockBits(GrayBmpData);
  47. if (Weight == ) return *(Scan0); // 说明所有的颜色值都相同
  48. return (byte)(SumWeight / Weight);
  49. }

  一般情况下,为了程序的速度考虑,对于一些小函数我建议直接自己展开,比如上面的ABS函数,直接写成if (Ex < 0) Ex = -Ex会快一些的。你通过下面的反汇编可以看出不同:

  1. Ex = Math.Abs(Ex);
  2. 00000161 js 00000167
  3. 00000163 mov eax,esi
  4. 00000165 jmp 0000016E
  5. 00000167 mov ecx,esi
  6. 00000169 call 638C54E4
  7. 0000016e mov esi,eax
  8. if (Ex < 0) Ex = -Ex;
  9. 00000170 test eax,eax
  10. 00000172 jge 00000176
  11. 00000174 neg esi

  

分割的效果可能还是要拿具体的图像说事,这里不做过多评论。

工程下载地址:http://files.cnblogs.com/Imageshop/ThresholdUseSIS.rar

博客园的网站分类里居然没有图像处理一栏,只有计算机图形学一项,其实搞这一行的都知道,这两个是完全不同的行业。希望博客园考虑增加图像处理一栏。

***************************作者: laviewpbt   时间: 2013.7.21    联系QQ:  33184777  转载请保留本行信息*************************

基于Simple Image Statistics(简单图像统计,SIS)的图像二值化算法。的更多相关文章

  1. Opencv实现图像的灰度处理,二值化,阀值选择

    前几天接触了图像的处理,发现用OPencv处理确实比較方便.毕竟是非常多东西都封装好的.可是要研究里面的东西,还是比較麻烦的,首先,你得知道图片处理的一些知识,比方腐蚀,膨胀,仿射,透射等,还有非常多 ...

  2. Win8 Metro(C#)数字图像处理--2.59 P分位法图像二值化

    原文:Win8 Metro(C#)数字图像处理--2.59 P分位法图像二值化  [函数名称]   P分位法图像二值化 [算法说明]   所谓P分位法图像分割,就是在知道图像中目标所占的比率Rat ...

  3. OpenCV:图像的普通二值化

    首先我们来看看图像二值化的过程,opencv一共有好几种不同的二值化算法可以使用,一般来说图像的像素,亮度等条件如果超过了某个或者低于了某个阈值,就会恒等于某个值,可以用于某些物体轮廓的监测: 导包: ...

  4. python实现图像二值化

    1.什么是图像二值化 彩色图像: 有blue,green,red三个通道,取值范围均为0-255 灰度图:只有一个通道0-255,所以一共有256种颜色 二值图像:只有两种颜色,黑色和白色,二值化就是 ...

  5. Java基于opencv实现图像数字识别(三)—灰度化和二值化

    Java基于opencv实现图像数字识别(三)-灰度化和二值化 一.灰度化 灰度化:在RGB模型中,如果R=G=B时,则彩色表示灰度颜色,其中R=G=B的值叫灰度值:因此,灰度图像每个像素点只需一个字 ...

  6. OpenCV_基于局部自适应阈值的图像二值化

    在图像处理应用中二值化操作是一个很常用的处理方式,例如零器件图片的处理.文本图片和验证码图片中字符的提取.车牌识别中的字符分割,以及视频图像中的运动目标检测中的前景分割,等等. 较为常用的图像二值化方 ...

  7. Win8 Metro(C#)数字图像处理--2.56简单统计法图像二值化

    原文:Win8 Metro(C#)数字图像处理--2.56简单统计法图像二值化  [函数名称] 简单统计法图像二值化 WriteableBitmap StatisticalThSegment(Wr ...

  8. opencv python 图像二值化/简单阈值化/大津阈值法

    pip install matplotlib 1简单的阈值化 cv2.threshold第一个参数是源图像,它应该是灰度图像. 第二个参数是用于对像素值进行分类的阈值, 第三个参数是maxVal,它表 ...

  9. 图像处理------基于Otsu阈值二值化

    一:基本原理 该方法是图像二值化处理常见方法之一,在Matlab与OpenCV中均有实现. Otsu Threshing方法是一种基于寻找合适阈值实现二值化的方法,其最重 要的部分是寻找图像二值化阈值 ...

随机推荐

  1. C#使用Graphics画圆写字

    画填充圆: Graphics gra = this.pictureBox1.CreateGraphics(); gra.SmoothingMode = System.Drawing.Drawing2D ...

  2. C#+arcengine10.0+SP5实现鹰眼(加载的是mdb数据库中的数据)

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  3. SQL SERVER 的模糊查询 LIKE

    今天写个动态脚本,需要把数据库里面包含“USER_"的表删除掉,突然想不起来如何搜索通配字符了,赶紧查查MSDN,整理了下模糊查询的知识点,留着以后查阅用. LIKE模糊查询的通配符 通配符 ...

  4. thinkphp验证码

    thinkphp自带验证码 前端页面: <div style="position:absolute;z-index:3;top:160px;left:180px;"> ...

  5. Java中2+2==5解读

    先来看一段程序,如下: package basic; import java.lang.reflect.Field; public class TestField { public static vo ...

  6. jquery插件图片延时加载实例详解

    效果预览:http://keleyi.com/keleyi/phtml/image/index.htm 使用方法:1.导入JS插件 <script src="http://keleyi ...

  7. JS操作未跨域iframe里的DOM

    这里简单说明两个方法,都是未跨域情况下在index.html内操作b.html内的 DOM. 如:index.html内引入iframe,在index内如何用JS操作iframe内的DOM元素? 先贴 ...

  8. angular源码分析:angular中入境检察官$sce

    一.ng-bing-html指令问题 需求:我需要将一个变量$scope.x = '<a href="http://www.cnblogs.com/web2-developer/&qu ...

  9. 【高级功能】使用canvas元素(第一部分)

    1. 开始使用 canvas 元素 canvas 元素非常简单,这是指它所有的功能都体现在一个JavaScript对象上,因此该元素本身只有两个属性:width 和 height. canvas 元素 ...

  10. javascript的函数

    1.函数的声明 (1) function命令方式 function fn(){}; (2) 函数的表达式 采用变量赋值的方式,function命令后面不带有函数名.如果加上函数名,那么该函数名只在函数 ...