1、 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 biYPe
2、lsPerMeter; 指定目标设备的垂直分辨率,单位是像素/米。 DWORD biClrUsed; 位图实际用到的颜色数,如果该值为零则用到的颜色数为2的biBitCount次幂。 DWORD biClrImportant; 位图显示过程中重要的颜色数,如果该值为零则认为所有的颜色都是重要的 BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;第3部分为颜色表。颜色表实际上是一个RGBQUAD结构的数组,数组的长度由biClrUsed指定(如果该值为零,则由biBitCount指定,即2的biBitCount次幂个元素)
3、。RGBQUAD结构是一个结构体类型,占4个字节,其定义如下:typedef struct tagRGBQUAD BYTE rgbBlue; 该颜色的蓝色分量; BYTE rgbGreen; 该颜色的绿色分量; BYTE rgbRed; 该颜色的红色分量; BYTE rgbReserved; 保留字节,暂不用。RGBQUAD;有些位图需要颜色表;有些位图(如真彩色图)则不需要颜色表,颜色表的长度由BITMAPINFOHEADER结构中biBitCount分量决定。对于biBitCount值为1的二值图像,每像素占1bit,图像中只有两种(如黑白)颜色,颜色表也就有21=2个表项,整个颜色表的大
4、小为8个字节;对于biBitCount值为8的灰度图像,每像素占8bit,图像中有28=256种颜色,颜色表也就有256个表项,且每个表项的R、G、B分量相等,整个颜色表的大小为1024个字节;而对于biBitCount=24的真彩色图像,由于每像素3个字节中分别代表了R、G、B三分量的值,此时不需要颜色表,因此真彩色图的BITMAPINFOHEADER结构后面直接就是位图数据。第4部分是位图数据,即图像数据,其紧跟在位图文件头、位图信息头和颜色表(如果有颜色表的话)之后,记录了图像的每一个像素值。对于有颜色表的位图,位图数据就是该像素颜色在调色板中的索引值;对于真彩色图,位图数据就是实际的R
5、、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个比特,其一个扫描行所占的真实字节数的计算公式如
6、下:DataSizePerLine = (biWidth * biBitCount /8+ 3) / 4*4那么,不压缩情况下位图数据的大小(BITMAPINFOHEADER结构中的biSizeImage成员)计算如下:biSizeImage = DataSizePerLine * biHeight第二,一般来说,BMP文件的数据是从图像的左下角开始逐行扫描图像的,即从下到上、从左到右,将图像的像素值一一记录下来,因此图像坐标零点在图像左下角。 好了,有了以上的知识后,可以开始压缩程序啦恩,现在问题是怎么读取那两个结构体,大家都知道c语言的文件读取有两种方式,一个是按字节读取,一个是按位读取,
7、那么用哪个呢?只好又XX了.好了,XX好了:二进制方式很简单,读文件时,会原封不动的读出文件的全部內容,写的時候,也是把內存缓冲区的內容原封不动的写到文件中。而文本方式就不一样了,在读文件时,会将换行符号CRLF(0x0D 0x0A)全部转换成单个的0x0A,并且当遇到结束符CTRLZ(0x1A)时,就认为文件已经结束。相应的,写文件时,会将所有的0x0A换成0x0D0x0A。 所以,若使用文本方式打开二进制文件时,就很容易出现文件读不完整,或內容不对的错误。即使是用文本方式打开文本文件,也要谨慎使用,比如复制文件,就不应该使用文本方式。(更多类容请自己XX)这里我们选择二进制的读入方式fre
8、ad(&h, sizeof(BITMAPFILEHEADER), 1, fp);以上以读取头部为例,其它的类似(注意要按顺序读取,不要弄反了)好了,到此第一步已经完成了我们把灰度信息读取到了一个数组里,那开始压缩了,这个按书上那个程序就好,压完得到一个l,b,分别记录每一段的长度和那一段每个像素需要的位数,特别提醒,书上的程序有一个bug!原文output函数里有一段是这样的:for(int j=1;j=m;j+)lj=lsj;bj=bsj;应该是for (j = 1; j = m; j+) lj = lsj; max = -1; for (i = sj - 1 + 1; i max) /bi
9、应该取第j段中占用位数最大的那个 max = le(pi); bj = max;bj不应该简单的赋值为bsj;而是应该取第j段中占用位数最大的那个。(太相信课本了,以至于开始的时候没仔细看它,浪费了好几个小时()另外要注意书上那个程序灰度值数组下标是从1开始的,这点要注意下(这个地方也死了1个多小时()好了现在我们得到两个数组l和b,分别记录每一段的长度和那一段每个像素需要的位数,好了开始将数据压缩到文件里吧,恩怎么压呢?当然是一段一段压啦,首先放入第一段的长度(8位)然后是每个像素所占的位数(3位)然后循环一下把这个段压下去,这些都不是8的倍数该怎么压呀猛一想确实挺头疼的。仔细想想我们只要用
10、一个变量j指示当前字节下标(也就是你要压缩到的那个数组),然后一个变量left指示当前字节还剩多少位可用这样比如说将一个占bit位的数据压入的代码是if (left (bit - left); aj +=(temp (8 + left - bit) & 0xff; left = 8 - (bit - left); else aj += (temp (left - bit) & left -= bit;这个自己在草稿纸演算一下应该没问题的,另外一个特别注意的地方,每次向左移位之后都要与oxff做个按位与操作,以获取低8位的数据,即使你声明temp是unsigned char型也一样,因为在表达式
11、中 char unsigned char short 都会被提升为int型,这叫做整型提升(我也不知道为什么,可能是便于运算看,这里又纠结了1个多小时,泪奔.)好了,灰度值我们知道怎么压缩了,下面就开始压缩吧,头部,头部信息,调试板不变,直接写进文件,额 不对,我们怎么知道压缩后的灰度值数组有多长? 还记得这个吗DWORD biSizeImage; 实际的位图数据占用的字节数,我们把压缩后的灰度值数组大小存进去,到时候解压的时候别忘了恢复哈。压缩完了,下面开始解压,解压就是压缩的逆过程同样的,我们用i指示当前要读取的字节,left指示当前字节剩余的可用位数,bit表示要读取的灰度值的位数,将结
12、果存到temp里面 if (left = bit) temp += (ai+ (8 - bit); temp += ai (8 - (bit - left); else temp += (ai (left - bit) 对运算符的优先级不了解的话还是老老实实加括号吧(同样的自己在草稿纸上演算一下)至此,大功告成了。将bmp四部分的信息按顺序写入文件就好了。下面贴上可以运行的代码,写得有点乱,有时间重构下.压缩程序#include stdlib.hstring.hwindows.h#define lmax 256 /压缩时每一段包含的最大像素数#define header 11 /每一段需要11
13、位来保存这一段的信息(8位用来表示段长度 /,3位用来表示每一像素占的位数)int bmpWidth; /图像的宽int bmpHeight; /图像的高RGBQUAD *pColorTable; /颜色表指针int biBitCount; /图像类型,每像素位数unsigned char *pBmpBuf; /灰度值数组int lineByte; /每一行的字节数BITMAPFILEHEADER h; /头部BITMAPINFOHEADER head; /头部信息int i, j; / 循环要用到的遍历变量int *s, *l, *b; /这三个数组的意义和书上是一样的int m; /分割的
14、段数 int le(int i) /log(i+1)向上取整 int k = 1; i = i / 2; while (i 0) k+; return k;void traceback(int n, int s, int l) /和书上一样不解释了 if (n = 0) return; traceback(n - ln, s, l); sm+ = n - ln;void compress(unsigned char p, int s, int l, int b)/这个也和书上一样 int n = bmpWidth * bmpHeight; int bmax; s0 = 0; for (i =
15、1;= n; i+) bi = le(pi); bmax = bi; si = si - 1 + bmax; li = 1; for (j = 2;= i &= lmax; if (bmax si - j + j * bmax) si = si - j + j * bmax; li = j; si += header;void collect(unsigned char p, int s, int l, int b) int max; traceback(n, s, l); sm = n; for (j = 1; i+) /特别注意这个地方,书上错了 max) /bi应该取第i段中占用位数最大
16、的那个void store(unsigned char *a, int *j, int *left, int bit, int temp)/将temp存到数组里 if (*left (bit - (*left); a(*j) +=(temp (8 + (*left) - bit) & *left = 8 - (bit - (*left); a(*j) += (temp (*left - bit) & *left -= bit;void compressToFile(char*file,unsigned char p, int l, int b, int m) /压缩存到文件里 FILE *ou
17、t = fopen(file, wb); int n = bmpWidth * bmpHeight, left, k = 1, t; unsigned char *a = (unsigned char*) malloc(n); memset(a, 0, n); left = 8; j = 0; store(a,&j,&left,8,li - 1);left,3,bi - 1); t = k + li; for (; k t; k+) left,bi,pk); head.biSizeImage = j + 1; /修改文件大小,这个解压图像中要用到 fwrite(&h, sizeof(BITMA
18、PFILEHEADER), 1, out);head, sizeof(BITMAPINFOHEADER), 1, out); fwrite(pColorTable, sizeof(RGBQUAD), 256, out); fwrite(a, 1, j + 1, out); fclose(out);unsigned char* readBmp(char *file) /读取文件 unsigned char*pBmpBuf; FILE *fp = fopen(file, rb fread(& /获取图片头信息head, sizeof(BITMAPINFOHEADER), 1, fp); /定义位图
19、信息头结构变量,读取位图信息头进内存,存放在变量head中 bmpWidth = head.biWidth; /获取图像宽、高、每像素所占位数等信息 bmpHeight = head.biHeight; biBitCount = head.biBitCount; lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;/定义变量,计算图像每行像素所占的字节数(必须是4的倍数) if (biBitCount = 8) pColorTable = (RGBQUAD*) malloc(sizeof(RGBQUAD) * 256);/申请颜色表所需要的空间
20、,读颜色表进内存 fread(pColorTable, sizeof(RGBQUAD), 256, fp); /灰度图像有颜色表,且颜色表表项为256 pBmpBuf = (unsigned char*) malloc(sizeof(unsigned char) * (lineByte /申请位图数据所需要的空间,读位图数据进内存 * bmpHeight + 1); fread(pBmpBuf + 1, 1, lineByte * bmpHeight, fp); /为了数组下标从1开始 fclose(fp); /关闭文件 return pBmpBuf;int main() char *file
21、 = input.bmp; pBmpBuf = readBmp(file); s = (int*) malloc(sizeof(int) * (bmpWidth * bmpHeight + 1); l = (int*) malloc(sizeof(int) * (bmpWidth * bmpHeight + 1); b = (int*) malloc(sizeof(int) * (bmpWidth * bmpHeight + 1); compress(pBmpBuf, s, l, b); collect(pBmpBuf, s, l, b); compressToFile(sqh.bmp,pBm
22、pBuf, l, b, m); return 0;解压程序 /要恢复灰度值数组unsigned char *a; /压缩的灰度值数组void load(unsigned char *a, int *i, int *left, int bit, int *temp)/将temp存到数组里 *temp += (a(*i)+ (8 - (bit - (*left); *temp += (a(*i) (*left) - bit) void craming() FILE *ss = fopen(restore.bmp, int left = 8, bit, length, k; int i = 0;/解
23、压前数组index int j = 0;/解压后数组index while (i head.biSizeImage) length = 0; bit = 0; load(a, &i, &left, 8, &length);left, 3, &bit); length+; /因为压缩的时候有减1 bit+; k = j; k + length;left, bit, &pBmpBufj); head.biSizeImage = bmpWidth * bmpHeight; /恢复像素所占的空间XX文库 - 让每个人平等地提升自我 fwrite(&h, sizeof(BITMAPFILEHEADER), 1, ss);head, sizeof(BITM
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1