问题:将一张bmp图像的灰度值压缩存储到一个中间文件,然后利用中间文件还原这张图片。
初一看,这应该是两个程序吧,一个压缩程序一个解压程序。那就先压缩好喽,恩,压缩...可是要怎么读取它的灰度值呀?文件里不会只保存它的灰度值吧,点开属性,发现这是一张256*192的图片,如果图片文件里只有灰度值,那么大小应该是256*192 B,而实际大小是50230字节。。可见还有其它信息,根据经验,应该还有一个对图像的描述信息吧,这样那些图像显示程序才能知道以怎样的方式去显示它,毕竟不是所有的bmp图片都是灰度图片。额,只好求助百度了.............经过整理,我把bmp图像编码格式发到下面。
BMP文件被分成4个部分:位图文件头(Bitmap File Header)、位图信息(BitmapInfoHeader)、颜表(Color Map)和位图数据(即图像数据,Data Bits或Data Body)
第1部分为位图文件头BITMAPFILEHEADER,是一个结构体类型,该结构的长度是固定的,为14个字节。其定义如下:
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; 位图文件类型,必须是0x424D,即字符串“BM”
DWORD bfSize; 位图文件大小,包括这14个字节。
WORD bfReserved1; Windows保留字,暂不用。
WORD bfReserved2; Windows保留字,暂不用。
DWORD bfOffBits; 从文件头到实际的位图数据的偏移字节数
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
第2部分为位图信息头BITMAPINFOHEADER,也是一个结构体类型的数据结构,该结构的长度也是固定的,为40个字节(WORD为无符号16位整数,DWORD为无符号32位整数,LONG为32位整数)。其定义如下:
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; 本结构的长度,为40个字节。
LONG biWidth; 位图的宽度,以像素为单位。
LONG biHeight; 位图的高度,以像素为单位.
WORD biPlanes; 目标设备的级别,必须是1。字符串数组怎么转成byte
WORD biBitCount 每个像素所占的位数(bit),其值必须为1(黑白图像)、4(16图)8 (256)、24(真彩图),新的BMP格式支持32位。
DWORD biCompression; 位图压缩类型,有效的值为BI_RGB(未经压缩)、BI_RLE8、BI_RLE4、 BI_BITFILEDS(均为Windows定义常量)。这里只讨论未经压缩的情况, 即biCompression=BI_RGB。
DWORD biSizeImage; 实际的位图数据占用的字节数
LONG biXPelsPerMeter; 指定目标设备的水平分辨率,单位是像素/米。
LONG biYPelsPerMeter; 指定目标设备的垂直分辨率,单位是像素/米。
DWORD biClrUsed; 位图实际用到的颜数,如果该值为零则用到的颜数为2的biBitCount次幂。
DWORD biClrImportant; 位图显示过程中重要的颜数,如果该值为零则认为所有的颜都是重要的
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
第3部分为颜表。颜表实际上是一个RGBQUAD结构的数组,数组的长度由biClrUsed指定(如果该值为零,则由biBitCount指定,即2的biBitCount次幂个元素)。RGBQUAD结构是一个结构体类型,占4个字节,其定义如下:
typedef struct tagRGBQUAD
{
BYTE rgbBlue; 该颜的蓝分量;
BYTE rgbGreen; 该颜的绿分量;
BYTE rgbRed; 该颜的红分量;
BYTE rgbReserved; 保留字节,暂不用。
}RGBQUAD;
有些位图需要颜表;有些位图(如真彩图)则不需要颜表,颜表的长度由BITMAPINFOHEADER结构中biBitCount分量决定。对于biBitCount值为1的二值图像,每像素占1bit,图像中只有两种(如黑白)颜,颜表也就有2^1=2个表项,整个颜表的大小为8个字节;对于biBitCount值为8的灰度图像,每像素占8bit,图像中有2^8=256种颜,颜表也就有256个表项,且每个表项的R、G、B分量相等,整个颜表的大小为1024个字节;而对于biBitCount=24的真彩图像,由于每像素3个字节中分别代表了R、G、B三分量的值,此时不需要颜表,因此真彩图的BITMAPINFOHEADER结构后面直接就是位图数据。
第4部分是位图数据,即图像数据,其紧跟在位图文件头、位图信息头和颜表(如果有颜表的话)之后,记录了图像的每一个像素值。对于有颜表的位图,位图数据就是该像素颜在调板中的索引值;对于真彩图,位图数据就是实际的R、G、B值(三个分量的存储顺序是B、G、R)。下面分别就2、16、256和真彩位图的位图数据进行说明:
— 对于2位图,用1位就可以表示该像素的颜,所以1个字节能存储8个像素的颜值。
— 对于16位图,用4位可以表示一个像素的颜。所以一个字节可以存储2个像素的颜值。
— 对于256位图,1个字节刚好存储1个像素的颜值。
— 对于真彩位图,3个字节才能表示1个像素的颜值。
需要注意两点:
第一,Windows规定一个扫描行所占的字节数必须是4的倍数,不足4的倍数则要对其进行扩充。假设图像的宽为biWidth个像素、每像素biBitCount个比特,其一个扫描行所占的真实字节数的计算公式如下:
DataSizePerLine = (biWidth * biBitCount /8+ 3) / 4*4
那么,不压缩情况下位图数据的大小(BITMAPINFOHEADER结构中的biSizeImage成员)计算如下:
biSizeImage = DataSizePerLine * biHeight
第二,一般来说,BMP文件的数据是从图像的左下角开始逐行扫描图像的,即从下到上、从左到右,将图像的像素值一一记录下来,因此图像坐标零点在图像左下角。
好了,有了以上的知识后,可以开始压缩程序啦~恩,现在问题是怎么读取那两个结构体,大家都知道c语言的文件读取有两种方式,一个是按字节读取,一个是按位读取,那么用哪个呢?只好又百度了................好了,百度好了:
二进制方式很简单,读文件时,会原封不动的读出文件的全部內容,写的時候,也是把內存缓冲区的內容原封不动的写到文件中。
而文本方式就不一样了,在读文件时,会将换行符号CRLF(0x0D 0x0A)全部转换成单个的0x
0A,并且当遇到结束符CTRLZ(0x1A)时,就认为文件已经结束。相应的,写文件时,会将所有的0x0A换成0x0D0x0A。 所以,若使用文本方式打开二进制文件时,就很容易出现文件读不完整,或內容不对的错误。即使是用文本方式打开文本文件,也要谨慎使用,比如复制文件,就不应该使用文本方式。(更多类容请自己百度)
这里我们选择二进制的读入方式
fread(&h, sizeof(BITMAPFILEHEADER), 1, fp);
以上以读取头部为例,其它的类似(注意要按顺序读取,不要弄反了)
好了,到此第一步已经完成了我们把灰度信息读取到了一个数组里,那开始压缩了,这个按书上那个程序就好,压完得到一个l[],b[],分别记录每一段的长度和那一段每个像素需要的位数,特别提醒,书上的程序有一个bug!!
原文output函数里有一段是这样的:
for(int j=1;j<=m;j++){l[j]=l[s[j]];b[j]=b[s[j]];}
应该是
for (j = 1; j <= m; j++) {
l[j] = l[s[j]];
max = -1;
for (i = s[j - 1] + 1; i <= s[j]; i++) {//特别注意这个地方书上错了
if (le(p[i]) > max) //b[i]应该取第j段中占用位数最大的那个
max = le(p[i]);
}
b[j] = max;
}
b[j]不应该简单的赋值为b[s[j]];而是应该取第j段中占用位数最大的那个。(太相信课本了,以至于开始的时候没仔细看它,浪费了好几个小时╮(╯▽╰)╭)另外要注意书上那个程序灰度值数组下标是从1开始的,这点要注意下(这个地方也死了1个多小时╮(╯▽╰)╭)好了现在我们得到两个数组l[]和b[],分别记录每一段的长度和那一段每个像素需要的位数,
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论