登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

易拉罐的博客

心静自然凉

 
 
 

日志

 
 

转 基于亮度的图像的二值化处理  

2010-04-08 22:29:24|  分类: 机器视觉 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |


图像的二值化简单来说就是把整张图片弄成只有两种颜色——通常是黑白两色。恩,图像处理的初阶呢。

通常是有很多方法,针对应用吧,这里只讨论简单的一种“战术”。通常我处理的是彩色图片而不是灰度图片,要将整张图片的丰富颜色统统划分为黑白,关键要确定分界线——难道是(127,127,127)?这很粗糙,应该考虑各个颜色的亮度。亮度不是RGB系统描述的,但是可以经过一段粗糙的转化。一种RGB颜色的亮度与各分量之间的关系:
IntenSity = 0.2990 * R + 0.5870 * G  + 0.1140 * B


void ImageBinaryzation::BmpBinary24_32(OperateBMP& image, int Hdis, int Wdis, int step)
{
      UCHAR blue, green, red;
      UCHAR result;
      int Hmax = (Hdis-1)*step;
 
      for(int j = 0; j < Hmax; j++)
        for(int i = 0; i < Wdis; i++)
        {
            blue =  image.GetImageData().buffer[i*step + 0 + Wdis * j];
            green = image.GetImageData().buffer[i*step + 1 + Wdis * j];
            red =   image.GetImageData().buffer[i*step + 2 + Wdis * j];
 
            result = 0.2990*red+0.5870*green+0.1140*blue;
 
            if(result>128)
                result = 255;
            else if(result<=128)
                result = 0;
 
                  image.GetImageData().buffer[i*step + 0 + Wdis * j] 
                = image.GetImageData().buffer[i*step + 1 + Wdis * j]
                = image.GetImageData().buffer[i*step + 2 + Wdis * j] 
                = result;
 
        }
}

 


如之前所说,逐个读图片的像素,逐个处理的手段在图像处理里是经常有的。这里step就是BMPBIT/8,对于24BIT图片是3(因为数据区每三个数据为一组,接下来移动3单位读下组),32BIT是4(注意这里没用上ALPHA分量)。通常边界问题不是那么重要.....以上操作一看就明白,最后得出的亮度两分后赋给原像素。当然如果不两分而直接给的话,就直接是张类似灰度图的真·256色图了。


8BIT的版本涉及读调色板:

 
void ImageBinaryzation::BmpBinary8(OperateBMP& image, int Hdis, int Wdis)
{
    UCHAR data = 0;
    UCHAR WhiteIndex = 0;
    UCHAR BlackIndex = 0;
 
    UCHAR blue, green, red;
    UCHAR result;
 
    for(int k = 0; k <256; k++)
    {
             red   =   image.GetImageData().palette[k].peRed;
             green =   image.GetImageData().palette[k].peGreen;
             blue  =   image.GetImageData().palette[k].peBlue;
             
             if(red == 0 && green == 0 && blue == 0)
                 BlackIndex = k;
             else if(red == 255 && green == 255 && blue == 255)
                 WhiteIndex = k;
    }
 
      for(int j = 0; j < Hdis; j++)
        for(int i = 0; i < Wdis; i++)
        {
            data =  image.GetImageData().buffer[i + Wdis * j];
          
             red   =   image.GetImageData().palette[data].peRed& 0xFF;
             green =   image.GetImageData().palette[data].peGreen& 0xFF;
             blue  =   image.GetImageData().palette[data].peBlue& 0xFF;     
 
            result = 0.2990*red+0.5870*green+0.1140*blue;
 
            if(result>128)
                result = WhiteIndex;             
            else if(result<=128)
                result = BlackIndex;
 
 
           image.GetImageData().buffer[i + Wdis * j] = result;
        }
 
        HBITMAP temp = CreateDIBitmap(image.GetDrawDeviceHandler(),
           &image.GetImageData().bitmapinfoheader,  CBM_INIT, image.GetImageData().buffer, 
            (BITMAPINFO*)&image.GetImageData().bitmapinfoheader, DIB_RGB_COLORS);
 
         image.SetBitmapHandler(temp);
 
}


首先是遍历调色板,找出黑色和白色对应的索引。然后遍历数据区,根据数据区里每个BIT的值调出调色板对应的值的RGB描述。接下来就跟之前一样了。但最后要刷新DDB,不然系统调色板不帮你弄的~方法是重新调用CreateDIBitmap建立DDB,并用HBITMAP描述,把该HBITMAP覆盖原来那个,关联到显示函数。
16位版本是比较麻烦的,先不说565格式的16BITBMP我还没载入显示成功,555格式的16BIT图片的像素读取也挺麻烦的。关键是设定好掩码后移位。要弄清楚的是,16BIT图片每组为2BYTE,因此读取时的步长是2。读出来的这16个BIT,前一位不管,通过掩码把它和后10位屏蔽掉,然后就是对应B分量有意义的那5位了,移位后就可以读取它了,其他分量类似(注意是*BGR顺序)。


void ImageBinaryzation::BmpBinary16(OperateBMP& image, int Hdis, int Wdis)
{
    UCHAR data1,data2;
    WORD data = 0;
 
    UCHAR blue, green, red;
    UCHAR result;
    int Hmax = (Hdis-1)*2;
 
 
      for(int j = 0; j < Hmax; j++)
        for(int i = 0; i < Wdis; i++)
        {
            data1 =  image.GetImageData().buffer[i*2 + 0 + Wdis * j];
            data2 =  image.GetImageData().buffer[i*2 + 1 + Wdis * j];
            data  =  data1 | data2<<8; //16BIT图片一次处理两字节,注意数据是低位在前高位在后
 
        if(image.GetImageData().bitmapinfoheader.biCompression == BI_RGB) //*555格式
        {
            blue  = ( (data&0x7C00)>>10 )<<3;
            green = ( (data&0x03E0)>>5  )<<3;   
            red   =  data&0x001F<<3;   
        }
        else if(image.GetImageData().bitmapinfoheader.biCompression == BI_BITFIELDS)
        {
            blue  = ( (data&0xF800)>>11 )<<3;
            green = ( (data&0x07E0)>>5  )<<3;   
            red   =  data&0x001F<<3;   
        }
 
 
            result = 0.2990*red+0.5870*green+0.1140*blue;
 
            if(result>128)
                result = 255;             
            else if(result<=128)
                result = 0;
 
        if(image.GetImageData().bitmapinfoheader.biCompression == BI_RGB) //*555格式
             data = ((result>>3)<<10) | ((result>>3)<<5) | (result>>3);
 
        else if(image.GetImageData().bitmapinfoheader.biCompression == BI_BITFIELDS)
             data = ((result>>3)<<11) | ((result>>3)<<5) | (result>>3);
 
              image.GetImageData().buffer[i*2 + 0 + Wdis * j] = LOBYTE(data);
              image.GetImageData().buffer[i*2 + 1 + Wdis * j] = HIBYTE(data);
 
 
 
        }
}


实现出来的只有BI_RGB) //*555格式,看看是怎样先把两个BYTE的数据合成一个WORD的(因为要在一个WORD上划分才行~),然后掩码+移位。注意移位到底端后要再向上移3位。因为对一个描述分量的UCHAR来说数据要满足占好一个BYTE(8IT)。正因为16BIT对全彩色是有损的,数据的有损(低位缺损)也是必然的咯。求出亮度,应用到颜色分量的时候,记得要复位。



其实我是真不想天空也有部分变黑的。但它在恰好亮度分界线(128)以下,可见实际应用中分界线的选取其实很重要的。

 

本文来源于ZwqXin http://www.zwqxin.com/ , 转载请注明
原文地址:http://www.zwqxin.com/archives/image-processing/image-intensity-binarization.html

  评论这张
 
阅读(675)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018