文档章节

基于Simple Image Statistics(简单图像统计,SIS)的图像二值化算法。

abcijkxyz
 abcijkxyz
发布于 2016/11/22 16:41
字数 1536
阅读 7
收藏 0

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

     算法过程简单描述如下: 

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

       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# 的代码中有相关的说明。 

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

  private bool IsGrayBitmap(Bitmap Bmp)
    {
        bool IsGray;
        if (Bmp.PixelFormat == PixelFormat.Format8bppIndexed)           // .net中灰度首先必然是索引图像
        {
            IsGray = true;
            if (Bmp.Palette.Entries.Length != 256)                      // 这个要求其实在PS中是不存在的
                IsGray = false;
            else
            {
                for (int X = 0; X < Bmp.Palette.Entries.Length; X++)    // 看看调色板的每一个分两值是不是都相等,且必须还要等于其在调色板中出现的顺序
                {
                    if (Bmp.Palette.Entries[X].R != X || Bmp.Palette.Entries[X].G != X || Bmp.Palette.Entries[X].B != X)
                    {
                        IsGray = false;
                        break;
                    }
                }
            }
        }
        else
        {
            IsGray = false;
        }
        return IsGray;
    }

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

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

private Bitmap ConvertToGrayModeBitmap(Bitmap Bmp)
    {
        int X, Y, SrcStride, DestStride, Width, Height;
        byte* SrcData, DestData;
        BitmapData BmpData = Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        Bitmap GrayBmp = new Bitmap(Bmp.Width, Bmp.Height, PixelFormat.Format8bppIndexed);
        ColorPalette Pal = GrayBmp.Palette;
        for (Y = 0; Y < Pal.Entries.Length; Y++) Pal.Entries[Y] = Color.FromArgb(255, Y, Y, Y);            // 设置灰度图像的调色板
        GrayBmp.Palette = Pal;

        //  LockBits 在第一个参数和图像一样大,以及读取格式和原始一样的情况下,调用函数的时间为0,且每次调用后BitmapData的Scan0都相同,而在
        // 其他的大部分情况下同样参数调用该函数返回的Scan0都不同,这就说明在在程序内部,GDI+为在创建图像时还是分配了和对应位图一样大小内存空间,
        // 这样我们就可以再加载时调用一次该函数,并记住Scan0的值,然后直接用指针操作这一片区域,就相当于操作了图像。而不用每次都LOCK和UNLOCK了
        // 从这个层次上说,该函数和GetDibits类似。

        BitmapData GrayBmpData = GrayBmp.LockBits(new Rectangle(0, 0, GrayBmp.Width, GrayBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
        Width = BmpData.Width; Height = BmpData.Height;
        SrcStride = BmpData.Stride; DestStride = GrayBmpData.Stride;           //  这个值并不一定就等于width*height*色深/8
        for (Y = 0; Y < Height; Y++)
        {
            SrcData = (byte*)BmpData.Scan0 + Y * SrcStride;                  // 必须在某个地方开启unsafe功能,其实C#中的unsafe很safe,搞的好吓人。
            DestData = (byte*)GrayBmpData.Scan0 + Y * DestStride;
            for (X = 0; X < Width; X++)
            {
                *DestData = (byte)((*SrcData * 7472 + *(SrcData + 1) * 38469 + *(SrcData + 2) * 19595) >> 16);        //这里可以有不同的算法
                SrcData += 3;
                DestData++;
            }
        }
        Bmp.UnlockBits(BmpData);
        GrayBmp.UnlockBits(GrayBmpData);
        return GrayBmp;
    }

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

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

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

    private byte GetSimpleStatisticsThreshold(Bitmap GrayBmp)
    {
        int Width, Height, Stride, X, Y;
        int CloneStride, Ex, Ey;
        int Weight = 0;
        long SumWeight = 0;                 //  对于大图像这个数字会溢出,所以用long类型的变量
        byte* Pointer, Scan0, CloneData;

        BitmapData GrayBmpData = GrayBmp.LockBits(new Rectangle(0, 0, GrayBmp.Width, GrayBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);

        Width = GrayBmp.Width; Height = GrayBmp.Height; Stride = GrayBmpData.Stride; CloneStride = Width + 2; Scan0 = (byte*)GrayBmpData.Scan0;
        CloneData = (byte*)GlobalAlloc(GPTR, CloneStride * (Height * 2));

        for (Y = 0; Y < Height; Y++)
        {
            *(CloneData + (Y + 1) * CloneStride) = *(Scan0 + Y * Stride);                           //  填充左侧第一列像素(不包括第一个和最后一个点)
            CopyMemory(CloneData + CloneStride * (Y + 1) + 1, Scan0 + Y * Stride, Width);
            *(CloneData + (Y + 1) * CloneStride + Width + 1) = *(Scan0 + Y * Stride + Width - 1);   //  填充最右侧那一列的数据
        }
        CopyMemory(CloneData, CloneData + CloneStride, CloneStride);                                //   第一行
        CopyMemory(CloneData + (Height + 1) * CloneStride, CloneData + Height * CloneStride, CloneStride);    //   最后一行

        for (Y = 0; Y < Height; Y++)
        {
            Pointer = CloneData + (Y + 1) * CloneStride + 1;
            for (X = 0; X < Width; X++)
            {
                Ex = *(Pointer - 1) - *(Pointer + 1);
                if (Ex < 0) Ex = -Ex;
                Ey = *(Pointer - CloneStride) - *(Pointer + CloneStride);
                if (Ey < 0) Ey = -Ey;
                if (Ex > Ey)
                {
                    Weight += Ex;
                    SumWeight += *Pointer * Ex;
                }
                else
                {
                    Weight += Ey;
                    SumWeight += *Pointer * Ey;
                }
                Pointer++;
            }
        }
        GlobalFree((IntPtr)CloneData);
        GrayBmp.UnlockBits(GrayBmpData);
        if (Weight == 0) return *(Scan0);  //  说明所有的颜色值都相同
        return (byte)(SumWeight / Weight);
    }

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

Ex = Math.Abs(Ex);
00000161  js          00000167 
00000163  mov         eax,esi 
00000165  jmp         0000016E 
00000167  mov         ecx,esi 
00000169  call        638C54E4 
0000016e  mov         esi,eax 
                if (Ex < 0) Ex = -Ex;
00000170  test        eax,eax 
00000172  jge         00000176 
00000174  neg         esi

  

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

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

 

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

 

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

本文转载自:http://www.cnblogs.com/Imageshop/p/3203599.html

共有 人打赏支持
abcijkxyz
粉丝 63
博文 6196
码字总数 1876
作品 0
深圳
项目经理
私信 提问
Apache SIS 0.3 发布,空间数据操作框架

Apache SIS 0.3 发布了,从该版本开始,SIS 算是转正成为 Apache 基金会的正式项目。Apache SIS 是一个空间的框架,可以更好地搜索,数据聚类,归档,或任何其他相关的空间坐标表示的需要。 ...

红薯
2013/08/21
1K
1
Apache SIS 0.7 发布,空间数据操作框架

Apache SIS 是一个空间的框架,可以更好地搜索,数据聚类,归档,或任何其他相关的空间坐标表示的需要。 Apache SIS 0.7 发布,此版本现已提供下载。 概要 Apache SIS 0.7 的主要新特性: Cr...

oschina
2016/05/29
1K
0
Apache SIS 0.5 发布,空间数据操作框架

Apache SIS 0.5 发布了,Apache SIS 是一个空间的框架,可以更好地搜索,数据聚类,归档,或任何其他相关的空间坐标表示的需要。 该版本改进内容包括: Improvements [SIS-79] - Implement ...

oschina
2015/02/13
1K
0
极简图片服务器 - simple-image-server

sis simple image server 为简单而生 如今的互联网时代图片存储服务随处可见,实现方案也是五花八门,那么有没有一个开袋即食的方案呢?粗略找了一圈,zimg似乎是我最想要的,但一看长长的依...

dwyer
08/11
0
0
阿里 SimpleImage 的使用?

请问大神有人用过这个吗? 阿里的开源SimpleImage,压缩图片的,jar下载下来了,有api,不知道用哪些类去实现压缩。有神给指点吗?急..

iwantup
2015/06/18
1K
1

没有更多内容

加载失败,请刷新页面

加载更多

《大漠烟尘》读书笔记及读后感文章3700字

《大漠烟尘》读书笔记及读后感文章3700字: 在这个浮躁的社会里,你有多久没有好好读完一本书了? 我们总觉得自己和别人不一样,所以当看到别人身上的问题时,很少有“反求诸己”,反思自己。...

原创小博客
43分钟前
1
0
大数据教程(9.5)用MR实现sql中的jion逻辑

上一篇博客讲解了使用jar -jar的方式来运行提交MR程序,以及通过修改YarnRunner的源码来实现MR的windows开发环境提交到集群的方式。本篇博主将分享sql中常见的join操作。 一、需求 订单数据表...

em_aaron
51分钟前
1
0
十万个为什么之什么是resultful规范

起源 越来越多的人开始意识到,网站即软件,而且是一种新型的软件。这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(high latency)、高并发等特点...

尾生
57分钟前
1
0
Terraform配置文件(Terraform configuration)

Terraform配置文件 翻译自Terraform Configuration Terraform用文本文件来描述设备、设置变量。这些文件被称为Terraform配置文件,以.tf结尾。这一部分将讲述Terraform配置文件的加载与格式。...

buddie
今天
2
0
exportfs命令, vsftp搭建ftp服务

exportfs命令 当修改/etc/exports文件后,更改的内容是不会立即生效的。如果重启nfs服务,会导致客户端重启期间的请求是挂起等待的,可以把客户端的挂载umount进行卸载后,再重启nfs服务,但...

野雪球
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部