您的位置:首页 > 其它

24位位图转化为灰度图

2007-06-18 16:30 239 查看
前两天刚刚开始做图像处理的东西,好不容易弄清了位图的基本格式,并尝试着编了一个二十四位位图转化为灰度图的API函数:

HDIB WINAPI DIBToGray(LPSTR lpDIB)
{
// 获取位图的信息头
LPBITMAPINFOHEADER lpDIBHdr; // 指向BITMAPINFOHEADER的指针
lpDIBHdr=(LPBITMAPINFOHEADER)lpDIB;
LPSTR lpGray;

// 计算位图图的信息头、调色板和图形数据的大小, 并给灰度图分配内存
int dwInfo=lpDIBHdr->biSize;
int dwPal=::DIBNumColors((LPSTR)lpDIBHdr)*sizeof(RGBQUAD);
int dwData=lpDIBHdr->biSizeImage;
int dwGrayPal=dwPal;
if(dwPal==0)
{
dwGrayPal=256*sizeof(RGBQUAD);
dwData=lpDIBHdr->biHeight*lpDIBHdr->biWidth;

}
int sizeTotal=dwInfo+dwGrayPal+dwData;
HGLOBAL hGray=(HGLOBAL)::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeTotal);
if (hGray==0)
{
return NULL; //内存分配失败则返回NULL
}
lpGray = (LPSTR) ::GlobalLock(hGray);

// 创建灰度图的颜色表 计算每个像素点的灰度值,即求平均即可
LPLOGPALETTE lpGrayPal=(LPLOGPALETTE)(lpGray+40);

char * lpBits=FindDIBBits(lpDIB);
int rowLen=WIDTHBYTES(8*lpDIBHdr->biWidth);
BYTE* lpGrayBits=(BYTE*)(lpGrayPal)+dwGrayPal;
int aver=0;
int i,j,k;

if(24==lpDIBHdr->biBitCount)
{
lpGrayPal->palNumEntries=256;
lpGrayPal->palVersion=PALVERSION;
for(i=0;i<256;i++)
{
lpGrayPal->palPalEntry[i].peBlue=i;
lpGrayPal->palPalEntry[i].peGreen=i;
lpGrayPal->palPalEntry[i].peRed=i;
lpGrayPal->palPalEntry[i].peFlags=0;
}
for (i=0;i<lpDIBHdr->biHeight;i++)
{
for (j=0; j<rowLen; j++)
{
k=i*3*rowLen+3*j;
lpGrayBits[i*rowLen+j]=(lpBits[k]+lpBits[k+1]+lpBits[k+2])/3;
}
}

}

// 创建灰度图的信息头
LPBITMAPINFOHEADER lpGrayHdr=(LPBITMAPINFOHEADER)lpGray;
memcpy(lpGrayHdr, lpDIBHdr, dwInfo);
if(dwPal==0)
{
lpGrayHdr->biSizeImage=sizeTotal;
lpGrayHdr->biBitCount=8;
lpGrayHdr->biClrUsed=256;
}

::GlobalUnlock(hGray);
return (HDIB)hGray;

}

在 (BYTE*)(lpGrayPal)+dwGrayPal; 中,最初由于写成了(BYTE*)(lpGrayPal+dwGrayPal);造成了分配的内存不够,一开始是尝试着多分配了一些内存,但是读出来的图像发生了错位,后来发现,dwGrayPal事实上已经是代表了dwGrayPal*sizeof(LOGPALETTE)的大小了,故而发生了错位。由此可以发现,指针增量的类型是十分重要的!

本来以为前面的代码已经算是做完了,但是今天上午换了张图片时,发现得到的灰度图发生了严重的倾斜,并且在第二次灰度化(第二次调用这个函数的时候,发现就是一张纯黑色的图片)。分析原因如下:

纯黑色是因为第二次进入这个函数的时候,只是重新复制了信息头,而调色板和图片数据都没有进行任何操作。而图片倾斜,则是因为位图在存储的时候,如果每一行的像素值所占的字节数为4的倍数时,则正常存储;否则要在后端补零。昨天试验的图片因为其行数恰好是4的倍数,所以没有出现异常,而今天试验的图片不是4的倍数,所以发生了倾斜。

下面给出校正后的代码:

HDIB WINAPI DIBToGray(LPSTR lpDIB)
{
// 获取位图的信息头
LPBITMAPINFOHEADER lpDIBHdr; // 指向BITMAPINFOHEADER的指针
lpDIBHdr=(LPBITMAPINFOHEADER)lpDIB;
LPSTR lpGray;

// 计算位图图的信息头、调色板和图形数据的大小, 并给灰度图分配内存
int dwInfo=lpDIBHdr->biSize;
int dwPal=::DIBNumColors((LPSTR)lpDIBHdr)*sizeof(RGBQUAD);
int dwData=lpDIBHdr->biSizeImage;
int dwGrayPal=dwPal;
int rowLenDes=WIDTHBYTES(8*lpDIBHdr->biWidth);
if(dwPal==0)
{
dwGrayPal=256*sizeof(RGBQUAD);
dwData=lpDIBHdr->biHeight*rowLenDes;

}
int sizeTotal=dwInfo+dwGrayPal+dwData;
HGLOBAL hGray=(HGLOBAL)::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeTotal);
if (hGray==0)
{
return NULL; //内存分配失败则返回NULL
}
lpGray = (LPSTR) ::GlobalLock(hGray);

// 创建灰度图的颜色表 计算每个像素点的灰度值,即求平均即可
LPLOGPALETTE lpGrayPal=(LPLOGPALETTE)(lpGray+40);

char * lpBits=FindDIBBits(lpDIB);
int rowLenSr=0;
BYTE* lpGrayBits=(BYTE*)(lpGrayPal)+dwGrayPal;
int i,j,k;

if(24==lpDIBHdr->biBitCount)
{
lpGrayPal->palNumEntries=256;
lpGrayPal->palVersion=PALVERSION;
for(i=0;i<256;i++)
{
lpGrayPal->palPalEntry[i].peBlue=i;
lpGrayPal->palPalEntry[i].peGreen=i;
lpGrayPal->palPalEntry[i].peRed=i;
lpGrayPal->palPalEntry[i].peFlags=0;
}
rowLenSr=WIDTHBYTES(24*lpDIBHdr->biWidth);
for (i=0;i<lpDIBHdr->biHeight;i++)
{
for (j=0; j<lpDIBHdr->biWidth; j++)
{
k=i*rowLenSr+3*j;
lpGrayBits[i*rowLenDes+j]=(lpBits[k]+lpBits[k+1]+lpBits[k+2])/3;
}
for (j=lpDIBHdr->biWidth;j<rowLenDes;j++)
{
lpGrayBits[i*rowLenDes+j]=0;
}
}

}
else
{
memcpy((LPSTR)lpGrayPal,(LPSTR)lpDIBHdr+40,dwGrayPal+dwData);
}
// 创建灰度图的信息头
LPBITMAPINFOHEADER lpGrayHdr=(LPBITMAPINFOHEADER)lpGray;
memcpy(lpGrayHdr, lpDIBHdr, dwInfo);
if(dwPal==0)
{
lpGrayHdr->biSizeImage=dwData;
lpGrayHdr->biBitCount=8;
lpGrayHdr->biClrUsed=256;
}

::GlobalUnlock(hGray);
return (HDIB)hGray;
}

后来发现前面所给的代码得到的灰度图并不理想,查看相关书籍后才发现灰度图转化并不是简单地将RGB三个分量的值求平均,具体说明如下:除了RGB颜色表示方法外,还有一种称作YUV的表示方法,其中的Y分量是表示亮度,也就是灰度图中的值,而UV表示色差信号。RGB和YUV二者间的转化关系为:

Y=0.299*R+0.587*G+0.114*B

对于真彩色图则直接对数据进行处理,而对于其他类型的位图,则只要把调色板中的RGB分量全部用Y来替代就可以了,并给出更新后的代码:

HDIB WINAPI DIBToGray(LPSTR lpDIB)
{
// 获取位图的信息头
LPBITMAPINFOHEADER lpDIBHdr; // 指向BITMAPINFOHEADER的指针
lpDIBHdr=(LPBITMAPINFOHEADER)lpDIB;
LPSTR lpGray;

// 计算位图图的信息头、调色板和图形数据的大小, 并给灰度图分配内存
int dwInfo=lpDIBHdr->biSize;
int dwPal=::DIBNumColors((LPSTR)lpDIBHdr)*sizeof(RGBQUAD);
int dwData=lpDIBHdr->biSizeImage;
int dwGrayPal=dwPal;
int rowLenDes=WIDTHBYTES(8*lpDIBHdr->biWidth);
if(dwPal==0)
{
dwGrayPal=256*sizeof(RGBQUAD);
dwData=lpDIBHdr->biHeight*rowLenDes;

}
int sizeTotal=dwInfo+dwGrayPal+dwData;
HGLOBAL hGray=(HGLOBAL)::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeTotal);
if (hGray==0)
{
return NULL; //内存分配失败则返回NULL
}
lpGray = (LPSTR) ::GlobalLock(hGray);

// 创建灰度图的颜色表 计算每个像素点的灰度值,即求平均即可
LPLOGPALETTE lpGrayPal=(LPLOGPALETTE)(lpGray+40);

unsigned char * lpBits=(unsigned char *)FindDIBBits(lpDIB);
int rowLenSr=0;
BYTE* lpGrayBits=(BYTE*)(lpGrayPal)+dwGrayPal;
int i,j,k;

if(24==lpDIBHdr->biBitCount)
{
lpGrayPal->palNumEntries=256;
lpGrayPal->palVersion=PALVERSION;
for(i=0;i<256;i++)
{
lpGrayPal->palPalEntry[i].peBlue=i;
lpGrayPal->palPalEntry[i].peGreen=i;
lpGrayPal->palPalEntry[i].peRed=i;
lpGrayPal->palPalEntry[i].peFlags=0;
}
rowLenSr=WIDTHBYTES(24*lpDIBHdr->biWidth);
for (i=0;i<lpDIBHdr->biHeight;i++)
{
for (j=0; j<lpDIBHdr->biWidth; j++)
{

k=i*rowLenSr+3*j;
lpGrayBits[i*rowLenDes+j]=(BYTE)(0.114*lpBits[k]+0.587*lpBits[k+1]+0.299*lpBits[k+2]);
}
for (j=lpDIBHdr->biWidth;j<rowLenDes;j++)
{
lpGrayBits[i*rowLenDes+j]=0;
}
}

}
else
{
LPLOGPALETTE lpPal=(LPLOGPALETTE)((LPSTR)lpDIBHdr+40);
lpGrayPal->palNumEntries=DIBNumColors((LPSTR)lpDIBHdr);
lpGrayPal->palVersion=PALVERSION;
unsigned char r,g,b,aver;
for(i=0;i<lpGrayPal->palNumEntries;i++)
{
b=(unsigned char)lpPal->palPalEntry[i].peBlue;
g=(unsigned char)lpPal->palPalEntry[i].peGreen;
r=(unsigned char)lpPal->palPalEntry[i].peRed;
aver=(unsigned char)(0.114*b+0.587*g+0.299*r);
lpGrayPal->palPalEntry[i].peBlue=aver;
lpGrayPal->palPalEntry[i].peGreen=aver;
lpGrayPal->palPalEntry[i].peRed=aver;
lpGrayPal->palPalEntry[i].peFlags=0;
}

memcpy((LPSTR)lpGrayBits,(LPSTR)lpBits,dwData);
}

// 创建灰度图的信息头
LPBITMAPINFOHEADER lpGrayHdr=(LPBITMAPINFOHEADER)lpGray;
memcpy(lpGrayHdr, lpDIBHdr, dwInfo);
if(dwPal==0)
{
lpGrayHdr->biSizeImage=dwData;
lpGrayHdr->biBitCount=8;
lpGrayHdr->biClrUsed=256;
}

::GlobalUnlock(hGray);
return (HDIB)hGray;
}

当然,若是得到灰度图可以覆盖原来的24位位图,则可以直接在已有的图片存储区操作,不用开辟新的存储区,此时函数的传入参数可以改为引用类型或输出参数改为指针类型。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: