图像处理中,大部分的处理方法都需要事先把彩色图转换成灰度图才能进行相关的计算、识别。
彩色图转换灰度图的原理如下:
我们知道彩色位图是由R/G/B三个分量组成,其文件存储格式为
BITMAPFILEHEADER+BITMAPINFOHEADER,紧跟后面的可能是:
如果是24位真彩图,则每个点是由三个字节分别表示R/G/B,所以这里直接跟着图像的色彩信息;
如果是8位(256色),4位(16色),1位(单色)图,则紧跟后面的是调色板数据,一个RGBQUAD类型的数组,其长度由BITMAPINFOHEADER.biClrUsed来决定。
然后后面紧跟的才是图像数据(24位图是真实的图像数据,其他的则是调色板的索引数据)。
灰度图是指只含亮度信息,不含色彩信息的图象,就象我们平时看到的黑白照片:亮度由暗到明,变化是连续的。因此,要表示灰度图,就需要把亮度值进行 量化。通常划分成0到255共256个级别,其中0最暗(全黑),255最亮(全白)。在表示颜色的方法中,除了RGB外,还有一种叫YUV的表示方法, 应用也很多。电视信号中用的就是一种类似于YUV的颜色表示方法。在这种表示方法中,Y分量的物理含义就是亮度,Y分量包含了灰度图的所有信息,只用Y分 量就能完全能够表示出一幅灰度图来。
从 RGB 到 YUV 空间的 Y 转换公式为:
Y = 0.299R+0.587G+0.114B
在 WINDOWS 中,表示 16 位以上的图和以下的图有点不同; 16 位以下的图使用一个调色板来表示选择具体的颜色,调色板的每个单元是 4 个字节,其中一个透明度;而具体的像素值存储的是索引,分别是 1 、 2 、 4 、 8 位。 16 位以上的图直接使用像素表示颜色。
那么如何将彩色图转换为灰度图呢?
灰度图中有调色板,首先需要确定调色板的具体颜色取值。我们前面提到了,灰度图的三个分量相等。
当转换为 8 位的时候,调色板中有 256 个颜色,每个正好从 0 到 255 个,三个分量都相等。
当转换为 4 位的时候,调色板中 16 个颜色,等间隔平分 255 个颜色值,三个分量都相等。
当转换为 2 位的时候,调色板中 4 个颜色,等间隔平分 255 个颜色,三个分量相等。
当转换为 1 位的时候,调色板中两个颜色,是 0 和 255 ,表示黑和白。
将彩色转换为灰度时候,按照公式计算出对应的值,该值实际上是亮度的级别;亮度从 0 到 255 ;由于不同的位有不同的亮度级别,所以 Y 的具体取值如下:
Y = Y/ (1<<(8- 转换的位数 ));
所以,我们要转化成灰度图,并且存储成一幅可以看到的图像,需要做如下转换:
16位以上的图像不带调色板,只需要把图像数据按每个点的位数都转换成相同的灰度值即可
16位以下的图像,则需要修改调色板的数值,并且按照每个点所占位数修改灰度值索引即可。
修改调色版中的颜色,(两种不同的计算灰度值的算法)
r=g=b=(r+g+b)/3 或者 r=g=b=(0.30r+0.59g+0.11b)
---------------------------------------------------------------
LPBYTE lpGrayBit=(LPBYTE)lpGray;
if(IS_WIN30_DIB(lpBit))
{
lpGrayBit+=pHeader->biSize+256*sizeof(RGBQUAD);//将指针移到新建的灰度图像的数据区
lpBit+=sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);//这个移到原来图像的数据区
}
else
{
lpGrayBit+=pHeader->biSize+256*sizeof(RGBTRIPLE);
lpBit+=sizeof(BITMAPINFOHEADER)+256*sizeof(RGBTRIPLE);
}
int nCountBit=pHeader->biBitCount;
if(nCountBit>8)//if biBitCount great eight then translate
{
int nRGB=(nWide*nCountBit+31)/32*4-nWide*nCountBit/8;
int nGrey=(nWide*8+31)/32*4-nWide;
for(i=0;i<nHeight;i++)
{
for(j=0;j<nWide;j++)
*lpGrayBit++=(*lpBit++)*0.299+(*lpBit++)*0.587+(*lpBit++)*0.114;
lpGrayBit+=nGrey;
lpBit+=nRGB;
}
::GlobalUnlock((HGLOBAL)hDib);
::GlobalFree((HGLOBAL)hDib);
hDib = hGreyDIB;
::GlobalUnlock((HGLOBAL)hGreyDIB);
}
注意调色板
---------------------------------------------------------------
//(14)各色位图转换成8位灰度图$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
void defLianBiao::TurnToGrey(LPSTR lpDib)
{
LPSTR lpNewDIBBits;// 指向DIB灰度图图像开始处象素的指针
unsigned char * ired;
unsigned char * igreen;
unsigned char * iblack;
long i,j;// 循环变量
unsigned char * lpdest;
//lpdest=(unsigned char *)::malloc(lHeight*lWidth);
lpdest= new unsigned char [lHeight*lWidth];
int n=0;
int PictureBits;
PictureBits=DIBBits(lpDib)/8;
if (PictureBits==0)//||PictureBits<=1)//不处理非整数字节的图像格式
{
AfxMessageBox("不支持该图像的数据格式");
return;
}
RGBQUAD *lpRGBquad;
lpRGBquad=(RGBQUAD *)&lpDib[sizeof(BITMAPINFOHEADER)];//位图信息头后面为调色板
unsigned char* lpSrc;
n=0;
if(PictureBits==1)//256色彩色位图
for(j=0;j<lHeight;j++)
for(i=0;i<lWidth;i++)
{
lpSrc = (unsigned char*)lpDIBBits + lLineBytes * j + i;
ired= &lpRGBquad[*lpSrc].rgbRed;
igreen =& lpRGBquad[*lpSrc].rgbGreen;
iblack = &lpRGBquad[*lpSrc].rgbBlue;
lpdest[n] =(unsigned char)(0.299*(*ired)+0.587*(*igreen)+0.114*(*iblack));
n++;
}
else//24/32位彩色位图
{
lLineBytes=WIDTHBYTES(lWidth * 8*PictureBits);
for (j=0;j<lHeight;j++)
for(i=0,n=0;i<PictureBits*lWidth;i+=PictureBits,n++)
{
ired= (unsigned char*)lpDIBBits + lLineBytes *j+i+2;
igreen = (unsigned char*)lpDIBBits+lLineBytes *j+i+1;
iblack = (unsigned char*)lpDIBBits +lLineBytes *j+i;
lpdest[j*lWidth+n] =(unsigned char)(0.299*(*ired)+0.587*(*igreen)+0.114*(*iblack));
}
}
LPBITMAPINFOHEADER lpBI;//位图信息头
// 读取BITMAPINFO结构,初始化指针
lpBI = (LPBITMAPINFOHEADER)lpDib;//[sizeof(BITMAPFILEHEADER)];
lpBI->biBitCount=8;
//设置256色灰度调色板
for (i = 0; i < 256; i++)
{
lpRGBquad[i].rgbRed =(unsigned char)i;// 读取红色分量
lpRGBquad[i].rgbGreen =(unsigned char)i;// 读取绿色分量
lpRGBquad[i].rgbBlue = (unsigned char)i;// 读取红色分量
lpRGBquad[i].rgbReserved = 0;// 保留位
}
lpNewDIBBits= ::FindDIBBits(lpDib);// 找到DIB图像象素起始位置
lLineBytes=WIDTHBYTES(lWidth * 8);
for(j=0;j<lHeight;j++)
for(i=0;i<lWidth;i++)
{
lpSrc=(unsigned char*)lpNewDIBBits+lLineBytes*j+i;
*lpSrc=lpdest[j*lWidth+i];
}
//::free((void *)lpdest);
delete lpdest;
}