« 认识HBITMAP与Bmp操作(整内存拷贝版)一年前,首次献给OpenGL之夜.雷达追踪 »

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

图像的二值化简单来说就是把整张图片弄成只有两种颜色——通常是黑白两色。恩,图像处理的初阶呢。——ZwqXin.com上两篇为:
Bmp文件的结构与基本操作(逐像素印屏版)
认识HBITMAP与Bmp操作(整内存拷贝版)

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

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

IntenSity = 0.2990 * R + 0.5870 * G  + 0.1140 * B

用上篇的方法载入BMP后,对其处理如下(24BIT,32BIT)

  1. void ImageBinaryzation::BmpBinary24_32(OperateBMP& image, int Hdis, int Wdis, int step)
  2. {
  3.       UCHAR blue, green, red;
  4.       UCHAR result;
  5.       int Hmax = (Hdis-1)*step;
  6.  
  7.       for(int j = 0; j < Hmax; j++)
  8.         for(int i = 0; i < Wdis; i++)
  9.         {
  10.             blue =  image.GetImageData().buffer[i*step + 0 + Wdis * j];
  11.             green = image.GetImageData().buffer[i*step + 1 + Wdis * j];
  12.             red =   image.GetImageData().buffer[i*step + 2 + Wdis * j];
  13.  
  14.             result = 0.2990*red+0.5870*green+0.1140*blue;
  15.  
  16.             if(result>128)
  17.                 result = 255;
  18.             else if(result<=128)
  19.                 result = 0;
  20.  
  21.                   image.GetImageData().buffer[i*step + 0 + Wdis * j] 
  22.                 = image.GetImageData().buffer[i*step + 1 + Wdis * j]
  23.                 = image.GetImageData().buffer[i*step + 2 + Wdis * j] 
  24.                 = result;
  25.  
  26.         }
  27. }

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

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

  1. void ImageBinaryzation::BmpBinary8(OperateBMP& image, int Hdis, int Wdis)
  2. {
  3.     UCHAR data = 0;
  4.     UCHAR WhiteIndex = 0;
  5.     UCHAR BlackIndex = 0;
  6.  
  7.     UCHAR blue, green, red;
  8.     UCHAR result;
  9.  
  10.     for(int k = 0; k <256; k++)
  11.     {
  12.              red   =   image.GetImageData().palette[k].peRed;
  13.              green =   image.GetImageData().palette[k].peGreen;
  14.              blue  =   image.GetImageData().palette[k].peBlue;
  15.              
  16.              if(red == 0 && green == 0 && blue == 0)
  17.                  BlackIndex = k;
  18.              else if(red == 255 && green == 255 && blue == 255)
  19.                  WhiteIndex = k;
  20.     }
  21.  
  22.       for(int j = 0; j < Hdis; j++)
  23.         for(int i = 0; i < Wdis; i++)
  24.         {
  25.             data =  image.GetImageData().buffer[i + Wdis * j];
  26.           
  27.              red   =   image.GetImageData().palette[data].peRed& 0xFF;
  28.              green =   image.GetImageData().palette[data].peGreen& 0xFF;
  29.              blue  =   image.GetImageData().palette[data].peBlue& 0xFF;     
  30.  
  31.             result = 0.2990*red+0.5870*green+0.1140*blue;
  32.  
  33.             if(result>128)
  34.                 result = WhiteIndex;             
  35.             else if(result<=128)
  36.                 result = BlackIndex;
  37.  
  38.  
  39.            image.GetImageData().buffer[i + Wdis * j] = result;
  40.         }
  41.  
  42.         HBITMAP temp = CreateDIBitmap(image.GetDrawDeviceHandler(),
  43.            &image.GetImageData().bitmapinfoheader,  CBM_INIT, image.GetImageData().buffer, 
  44.             (BITMAPINFO*)&image.GetImageData().bitmapinfoheader, DIB_RGB_COLORS);
  45.  
  46.          image.SetBitmapHandler(temp);
  47.  
  48. }

首先是遍历调色板,找出黑色和白色对应的索引。然后遍历数据区,根据数据区里每个BIT的值调出调色板对应的值的RGB描述。接下来就跟之前一样了。但最后要刷新DDB,不然系统调色板不帮你弄的~方法是重新调用CreateDIBitmap建立DDB,并用HBITMAP描述,把该HBITMAP覆盖原来那个,关联到显示函数。

16位版本是比较麻烦的,先不说565格式的16BITBMP我还没载入显示成功,555格式的16BIT图片的像素读取也挺麻烦的。关键是设定好掩码后移位。要弄清楚的是,16BIT图片每组为2BYTE,因此读取时的步长是2。读出来的这16个BIT,前一位不管,通过掩码把它和后10位屏蔽掉,然后就是对应B分量有意义的那5位了,移位后就可以读取它了,其他分量类似(注意是*BGR顺序)。

  1. void ImageBinaryzation::BmpBinary16(OperateBMP& image, int Hdis, int Wdis)
  2. {
  3.     UCHAR data1,data2;
  4.     WORD data = 0;
  5.  
  6.     UCHAR blue, green, red;
  7.     UCHAR result;
  8.     int Hmax = (Hdis-1)*2;
  9.  
  10.  
  11.       for(int j = 0; j < Hmax; j++)
  12.         for(int i = 0; i < Wdis; i++)
  13.         {
  14.             data1 =  image.GetImageData().buffer[i*2 + 0 + Wdis * j];
  15.             data2 =  image.GetImageData().buffer[i*2 + 1 + Wdis * j];
  16.             data  =  data1 | data2<<8; //16BIT图片一次处理两字节,注意数据是低位在前高位在后
  17.  
  18.         if(image.GetImageData().bitmapinfoheader.biCompression == BI_RGB) //*555格式
  19.         {
  20.             blue  = ( (data&0x7C00)>>10 )<<3;
  21.             green = ( (data&0x03E0)>>5  )<<3;   
  22.             red   =  data&0x001F<<3;   
  23.         }
  24.         else if(image.GetImageData().bitmapinfoheader.biCompression == BI_BITFIELDS)
  25.         {
  26.             blue  = ( (data&0xF800)>>11 )<<3;
  27.             green = ( (data&0x07E0)>>5  )<<3;   
  28.             red   =  data&0x001F<<3;   
  29.         }
  30.  
  31.  
  32.             result = 0.2990*red+0.5870*green+0.1140*blue;
  33.  
  34.             if(result>128)
  35.                 result = 255;             
  36.             else if(result<=128)
  37.                 result = 0;
  38.  
  39.         if(image.GetImageData().bitmapinfoheader.biCompression == BI_RGB) //*555格式
  40.              data = ((result>>3)<<10) | ((result>>3)<<5) | (result>>3);
  41.  
  42.         else if(image.GetImageData().bitmapinfoheader.biCompression == BI_BITFIELDS)
  43.              data = ((result>>3)<<11) | ((result>>3)<<5) | (result>>3);
  44.  
  45.               image.GetImageData().buffer[i*2 + 0 + Wdis * j] = LOBYTE(data);
  46.               image.GetImageData().buffer[i*2 + 1 + Wdis * j] = HIBYTE(data);
  47.  
  48.  
  49.  
  50.         }
  51. }

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

 原图片:
图像二值化-----www.zwqxin.com
处理后:
图像二值化-----www.zwqxin.com
其实我是真不想天空也有部分变黑的。但它在恰好亮度分界线(128)以下,可见实际应用中分界线的选取其实很重要的。

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

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2024 ZwqXin. All Rights Reserved. Theme edited from ipati.