图像的二值化简单来说就是把整张图片弄成只有两种颜色——通常是黑白两色。恩,图像处理的初阶呢。
通常是有很多方法,针对应用吧,这里只讨论简单的一种“战术”。通常我处理的是彩色图片而不是灰度图片,要将整张图片的丰富颜色统统划分为黑白,关键要确定分界线——难道是(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 da
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++)
{
da
red = image.GetImageData().palette[da
green = image.GetImageData().palette[da
blue = image.GetImageData().palette[da
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 da
WORD da
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++)
{
da
da
da
if(image.GetImageData().bitmapinfoheader.biCompression == BI_RGB) //*555格式
{
blue = ( (da
green = ( (da
red = da
}
else if(image.GetImageData().bitmapinfoheader.biCompression == BI_BITFIELDS)
{
blue = ( (da
green = ( (da
red = da
}
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格式
da
else if(image.GetImageData().bitmapinfoheader.biCompression == BI_BITFIELDS)
da
image.GetImageData().buffer[i*2 + 0 + Wdis * j] = LOBYTE(da
image.GetImageData().buffer[i*2 + 1 + Wdis * j] = HIBYTE(da
}
}
实现出来的只有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
评论