32位bmp转8位设置调色板的问题

gudufuyun 2009-11-26 11:33:20
LPSTR lpDIB; // 指向DIB的指针
// 由DIB句柄得到DIB指针并锁定DIB
lpDIB = (LPSTR) ::GlobalLock((HGLOBAL)hDIB);
LPSTR lpDIBBits;// 指向DIB图像开始处象素指针
unsigned char * ired;
unsigned char * igreen;
unsigned char * iblue;
long lWidth,lHeight;// 图像宽度,图像高度
lWidth = ::DIBWidth(lpDIB); // 获取图像宽度
lHeight = ::DIBHeight(lpDIB);// 获取图像高度
lWidth =WIDTHBYTES(lWidth * 8);
lpDIBBits=::FindDIBBits(lpDIB);// 找到DIB图像象素起始位置
unsigned char * lpdest;
lpdest=(unsigned char *)::malloc(lHeight*lWidth);
int n=0;
for(int j = 0; j <4*lWidth*lHeight;n++,j=j+4)//读取图像数据并转化成灰度
{
ired= (unsigned char*)lpDIBBits+j;
igreen = (unsigned char*)lpDIBBits+j+1;
iblue = (unsigned char*)lpDIBBits +j+2;
lpdest[n] =(unsigned char)(0.299*(*ired)+0.587*(*igreen)+0.114*(*iblue)+0.5);
}
LPBITMAPINFOHEADER lpBI;//位图信息头
// 读取BITMAPINFO结构,初始化指针
lpBI = (LPBITMAPINFOHEADER)lpDIB; //[sizeof(BITMAPFILEHEADER)];
lpBI->biBitCount=8;
RGBQUAD *lpRGBquad;
lpRGBquad =(RGBQUAD *)&lpDIB[sizeof(BITMAPINFOHEADER)];//位图信息头后面为调色板
for (int 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;// 保留位
}
LPSTR lpNewDIBBits;// 指向DIB灰度图图像开始处象素的指针
lpNewDIBBits=::FindDIBBits(lpDIB);// 找到DIB图像象素起始位置
unsigned char * lpSrc;
for (i=0;i<lHeight*lWidth;i++) //写灰度图像数据
{
lpSrc=(unsigned char*)lpNewDIBBits+i;
*lpSrc=lpdest[i];
}

::free((void *)lpdest);
::GlobalUnlock ((HGLOBAL)hDIB);

运行后有问题。转化灰度的时候没有问题,去掉红色代码显示是正常的。但还是32位图。
设置调色板后,图像显示有问题。
请问是哪里的问题?

还有16位转化成灰度又该如何做呢?
...全文
938 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiaoxiao_sa 2011-06-13
  • 打赏
  • 举报
回复
学习了
ct1986 2010-08-22
  • 打赏
  • 举报
回复
学习了,都是高手呀
wsslcj 2010-05-22
  • 打赏
  • 举报
回复
学习了,都是高手呀
dvlinker 2009-12-24
  • 打赏
  • 举报
回复
学习了
cloudyi 2009-11-27
  • 打赏
  • 举报
回复
一点建议:
for(int j = 0; j <3*lWidth*lHeight;n++,j=j+3)//读取图像数据并转化成灰度
{
ired= (unsigned char*)lpDIBBits + j+2;
igreen = (unsigned char*)lpDIBBits+j+1;
iblack = (unsigned char*)lpDIBBits +j;
lpdest[n] =(unsigned char)(0.299*(*ired)+0.587*(*igreen)+0.114*(*iblack));
}

因为图像数据行数据长度是DWORD对齐的, 所以写成下面这样比较好
DWORD lLineBytes = (lWidth * 24 + 31)/32 * 4;
for(int i = 0; i < lHeight; i++)
{
for(int j = 0; j < lWidth; j ++)
{
ired = (unsigned char*)(lpDIBBits + lLineBytes * i + 3 * j + 2);
...;
}
}
gudufuyun 2009-11-26
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 happyparrot 的回复:]
你的红色代码不对啊。对于灰度图来说,调色板中存储的是真正的颜色值,而数据部分存储的只是对应的颜色值在调色板中的序号而已。所以你这么写:
for  (i=0;i <lHeight*lWidth;i++) //写灰度图像数据 

lpSrc=(unsigned  char*)lpNewDIBBits+i; 
*lpSrc=lpdest[i]; 

肯定是不对的。*lpSrc中的是调色板中的序号,不是颜色值。
[/Quote]

可是我这么写,24位转8位灰度是正确的啊
LPSTR lpDIB; // 指向DIB的指针
// 由DIB句柄得到DIB指针并锁定DIB
lpDIB = (LPSTR) ::GlobalLock((HGLOBAL)hDIB);
int NumColors;
NumColors=((LPBITMAPINFOHEADER)lpDIB)->biBitCount;
LPSTR lpDIBBits;// 指向DIB图像开始处象素指针
unsigned char * ired;
unsigned char * igreen;
unsigned char * iblack;
long lWidth,lHeight;// 图像宽度,图像高度
lWidth = ::DIBWidth(lpDIB); // 获取图像宽度
lHeight = ::DIBHeight(lpDIB);// 获取图像高度
lWidth =WIDTHBYTES(lWidth * 8);
lpDIBBits=::FindDIBBits(lpDIB);// 找到DIB图像象素起始位置&lpDIB[sizeof(BITMAPINFOHEADER)];
unsigned char * lpdest;
lpdest=(unsigned char *)::malloc(lHeight*lWidth);
int n=0;
for(int j = 0; j <3*lWidth*lHeight;n++,j=j+3)//读取图像数据并转化成灰度
{
ired= (unsigned char*)lpDIBBits + j+2;
igreen = (unsigned char*)lpDIBBits+j+1;
iblack = (unsigned char*)lpDIBBits +j;
lpdest[n] =(unsigned char)(0.299*(*ired)+0.587*(*igreen)+0.114*(*iblack));
// *ired=lpdest[n] ;
// *igreen=lpdest[n] ;
// *iblack=lpdest[n] ;
}
LPBITMAPINFOHEADER lpBI;//位图信息头
// 读取BITMAPINFO结构,初始化指针
lpBI = (LPBITMAPINFOHEADER)lpDIB;//[sizeof(BITMAPFILEHEADER)];
lpBI->biBitCount=8;
//设置256色灰度调色板
RGBQUAD *lpRGBquad;
lpRGBquad=(RGBQUAD *)&lpDIB[sizeof(BITMAPINFOHEADER)];//位图信息头后面为调色板
for (int 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;// 保留位
}
LPSTR lpNewDIBBits;// 指向DIB灰度图图像开始处象素的指针
lpNewDIBBits= ::FindDIBBits(lpDIB);// 找到DIB图像象素起始位置
unsigned char * lpSrc;
for (i=0;i<lHeight*lWidth;i++)//写灰度图像数据
{
lpSrc=(unsigned char*)lpNewDIBBits+i;
*lpSrc=lpdest[i];
}
::free((void *)lpdest);
::GlobalUnlock ((HGLOBAL)hDIB);
fandh 2009-11-26
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 happyparrot 的回复:]
你的红色代码不对啊。对于灰度图来说,调色板中存储的是真正的颜色值,而数据部分存储的只是对应的颜色值在调色板中的序号而已。所以你这么写:
for  (i=0;i <lHeight*lWidth;i++) //写灰度图像数据 

lpSrc=(unsigned  char*)lpNewDIBBits+i; 
*lpSrc=lpdest[i]; 

肯定是不对的。*lpSrc中的是调色板中的序号,不是颜色值。
1楼大牛既然已经出现,我就顶吧
快乐鹦鹉 2009-11-26
  • 打赏
  • 举报
回复
你的红色代码不对啊。对于灰度图来说,调色板中存储的是真正的颜色值,而数据部分存储的只是对应的颜色值在调色板中的序号而已。所以你这么写:
for (i=0;i <lHeight*lWidth;i++) //写灰度图像数据
{
lpSrc=(unsigned char*)lpNewDIBBits+i;
*lpSrc=lpdest[i];
}
肯定是不对的。*lpSrc中的是调色板中的序号,不是颜色值。
lidony 2009-11-26
  • 打赏
  • 举报
回复
biSize打错了,应该是bfSize
lidony 2009-11-26
  • 打赏
  • 举报
回复
哦,是灰度图啊,我搞错了。
1、8位灰度图和彩色图两个的调色板不一样。都是3个分量都有。灰度调色板就像你那样。
2、对于你的代码,我们看看内存都改了哪些:
(1).把lpBI->biBitCount=8,把位数改为8。
(2).然而灰度的调色板写入内存,是在lpRGBquad=(RGBQUAD *)&lpDIB[sizeof(BITMAPINFOHEADER)]开始写入的。然后计算出原图像像素的起始位置,因为24位和32位没有调色板,那么像素的起始位置就是lpRGBquad=(RGBQUAD *)&lpDIB[sizeof(BITMAPINFOHEADER)],写入新的灰度值,这样就把原来的调色板内容覆盖了。那为什么还能正确呢,来看看是你现在的bmp图片的:1.首先读取bm,发现是位图文件,2.然后比较BITMAPFILEHEADER的bfSize的大小是否相符,因为biSize的大小你并没有改,和以前的一样,并不会错。 3.然后读一些其他相关变量等等,都不会错。4.读数据,他是从BITMAPFILEHEADER的OffBits开始读的,因为你没改这个值,就是从原图像的像素起始位置开始的。读出一个字节的值,然后索引它的调色板,其实这儿的调色板就是你的数据区的值,因为是灰度图,可能看不出来是否正确。直到读到图像的大小。如果原图像较大的话,其实后面还有很多原图像的数据没读。如果图像小的话,后面还有的就是调色板的内容,因为小没覆盖完。所以图片显示感觉是正确的。我感觉是这样。


改变位其他位位图要改变的结构体变量很多的,都给改了才能算对。
gudufuyun 2009-11-26
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 lidony 的回复:]
首先说一下你的调色板就错了,8位位图的调色板不是这样的,它有256种颜色,不是像你那样全是灰度色,
你可以16进制打开一幅256色的bmp图片看看调色板。所以这儿:
for  (int 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;//  保留位 

是错误的。

然后数据区存的则是这256色的索引,转换的时候应该直接把调色板的内容从32位位图的数据区lpNewDIBBits=  ::FindDIBBits(lpDIB)开始赋值,因为32位是没有调色板的,文件头开始之后就是数据区。调色板赋值完后再开始赋值数据区,接着把相关变量赋值。所以这儿:
for  (i=0;i <lHeight*lWidth;i++)//写灰度图像数据 

lpSrc=(unsigned  char*)lpNewDIBBits+i; 
*lpSrc=lpdest[i]; 


也是错的,现在的数据区并不一定就从以前的像素起始位置开始,而是在调色板之后。
[/Quote]
大概明白了你的意思,谢谢了~
不过有几个问题还是不太清楚,我对bmp的数据格式看了很久,可能理解上有些偏差
1、我是转的8位灰度图像,8位灰度图像的调色板和8位彩色图像是一样的么?3个分量信息都有?还是只有灰度信息?
2、转换成8位图像后,由于加了调色板,数据区不一定从像素起始位置开始,而是在调色板之后。
恩,这么解释说得通,但有一个很直接的问题,关于24位转8位的,我那么写完后,图片可以转化,而且也是8位。如果按照这个解释,24位的也是没有调色板,转化完毕后头指针应该不是指向的像素起始位置,而是调色板之后啊?

我用的DIBAPI这个库,个人觉得FindDIBBits应该就是找到的像素起始位置指针把。因为在其他对8位图像操作中,直接用它找到的。
lidony 2009-11-26
  • 打赏
  • 举报
回复
首先说一下你的调色板就错了,8位位图的调色板不是这样的,它有256种颜色,不是像你那样全是灰度色,
你可以16进制打开一幅256色的bmp图片看看调色板。所以这儿:
for (int 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;// 保留位
}
是错误的。

然后数据区存的则是这256色的索引,转换的时候应该直接把调色板的内容从32位位图的数据区lpNewDIBBits= ::FindDIBBits(lpDIB)开始赋值,因为32位是没有调色板的,文件头开始之后就是数据区。调色板赋值完后再开始赋值数据区,接着把相关变量赋值。所以这儿:
for (i=0;i <lHeight*lWidth;i++)//写灰度图像数据
{
lpSrc=(unsigned char*)lpNewDIBBits+i;
*lpSrc=lpdest[i];
}

也是错的,现在的数据区并不一定就从以前的像素起始位置开始,而是在调色板之后。

19,468

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧