bmp图像压缩算法详细解析_第1页
bmp图像压缩算法详细解析_第2页
bmp图像压缩算法详细解析_第3页
bmp图像压缩算法详细解析_第4页
bmp图像压缩算法详细解析_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、精选优质文档-倾情为你奉上精选优质文档-倾情为你奉上专心-专注-专业专心-专注-专业精选优质文档-倾情为你奉上专心-专注-专业 问题:将一张bmp图像的灰度值压缩存储到一个中间文件,然后利用中间文件还原这张图片。 初一看,这应该是两个程序吧,一个压缩程序一个解压程序。那就先压缩好喽,恩,压缩.可是要怎么读取它的灰度值呀?文件里不会只保存它的灰度值吧,点开属性,发现这是一张256*192的图片,如果图片文件里只有灰度值,那么大小应该是256*192 B,而实际大小是50230字节。可见还有其它信息,根据经验,应该还有一个对图像的描述信息吧,这样那些图像显示程序才能知道以怎样的方式去显示它,毕竟不

2、是所有的bmp图片都是灰度图片。额,只好求助百度了.经过整理,我把bmp图像编码格式发到下面。 BMP文件被分成4个部分:位图文件头(Bitmap File Header)、位图信息(BitmapInfoHeader)、颜色表(Color Map)和位图数据(即图像数据,Data Bits或Data Body)第1部分为位图文件头BITMAPFILEHEADER,是一个结构体类型,该结构的长度是固定的,为14个字节。其定义如下:typedef struct tagBITMAPFILEHEADER WORD bfType; 位图文件类型,必须是0 x424D,即字符串“BM” DWORD bfS

3、ize; 位图文件大小,包括这14个字节。 WORD bfReserved1; Windows保留字,暂不用。 WORD bfReserved2; Windows保留字,暂不用。 DWORD bfOffBits; 从文件头到实际的位图数据的偏移字节数 BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;第2部分为位图信息头BITMAPINFOHEADER,也是一个结构体类型的数据结构,该结构的长度也是固定的,为40个字节(WORD为无符号16位整数,DWORD为无符号32位整数,LONG为32位整数)。其定义如下:type

4、def struct tagBITMAPINFOHEADER DWORD biSize; 本结构的长度,为40个字节。 LONG biWidth; 位图的宽度,以像素为单位。 LONG biHeight; 位图的高度,以像素为单位. WORD biPlanes; 目标设备的级别,必须是1。 WORD biBitCount 每个像素所占的位数(bit),其值必须为1(黑白图像)、4(16色图)8 (256色)、24(真彩色图),新的BMP格式支持32位色。 DWORD biCompression; 位图压缩类型,有效的值为BI_RGB(未经压缩)、BI_RLE8、BI_RLE4、 BI_BITF

5、ILEDS(均为Windows定义常量)。这里只讨论未经压缩的情况, 即biCompression=BI_RGB。 DWORD biSizeImage; 实际的位图数据占用的字节数 LONG biXPelsPerMeter; 指定目标设备的水平分辨率,单位是像素/米。 LONG biYPelsPerMeter; 指定目标设备的垂直分辨率,单位是像素/米。 DWORD biClrUsed; 位图实际用到的颜色数,如果该值为零则用到的颜色数为2的biBitCount次幂。 DWORD biClrImportant; 位图显示过程中重要的颜色数,如果该值为零则认为所有的颜色都是重要的 BITMAPI

6、NFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;第3部分为颜色表。颜色表实际上是一个RGBQUAD结构的数组,数组的长度由biClrUsed指定(如果该值为零,则由biBitCount指定,即2的biBitCount次幂个元素)。RGBQUAD结构是一个结构体类型,占4个字节,其定义如下:typedef struct tagRGBQUAD BYTE rgbBlue; 该颜色的蓝色分量; BYTE rgbGreen; 该颜色的绿色分量; BYTE rgbRed; 该颜色的红色分量; BYTE rgbReserved; 保留字节,暂不用

7、。RGBQUAD;有些位图需要颜色表;有些位图(如真彩色图)则不需要颜色表,颜色表的长度由BITMAPINFOHEADER结构中biBitCount分量决定。对于biBitCount值为1的二值图像,每像素占1bit,图像中只有两种(如黑白)颜色,颜色表也就有21=2个表项,整个颜色表的大小为8个字节;对于biBitCount值为8的灰度图像,每像素占8bit,图像中有28=256种颜色,颜色表也就有256个表项,且每个表项的R、G、B分量相等,整个颜色表的大小为1024个字节;而对于biBitCount=24的真彩色图像,由于每像素3个字节中分别代表了R、G、B三分量的值,此时不需要颜色表,

8、因此真彩色图的BITMAPINFOHEADER结构后面直接就是位图数据。第4部分是位图数据,即图像数据,其紧跟在位图文件头、位图信息头和颜色表(如果有颜色表的话)之后,记录了图像的每一个像素值。对于有颜色表的位图,位图数据就是该像素颜色在调色板中的索引值;对于真彩色图,位图数据就是实际的R、G、B值(三个分量的存储顺序是B、G、R)。下面分别就2色、16色、256色和真彩色位图的位图数据进行说明: 对于2色位图,用1位就可以表示该像素的颜色,所以1个字节能存储8个像素的颜色值。 对于16色位图,用4位可以表示一个像素的颜色。所以一个字节可以存储2个像素的颜色值。 对于256色位图,1个字节刚好

9、存储1个像素的颜色值。 对于真彩色位图,3个字节才能表示1个像素的颜色值。需要注意两点:第一,Windows规定一个扫描行所占的字节数必须是4的倍数,不足4的倍数则要对其进行扩充。假设图像的宽为biWidth个像素、每像素biBitCount个比特,其一个扫描行所占的真实字节数的计算公式如下:DataSizePerLine = (biWidth * biBitCount /8+ 3) / 4*4那么,不压缩情况下位图数据的大小(BITMAPINFOHEADER结构中的biSizeImage成员)计算如下:biSizeImage = DataSizePerLine * biHeight第二,一般

10、来说,BMP文件的数据是从图像的左下角开始逐行扫描图像的,即从下到上、从左到右,将图像的像素值一一记录下来,因此图像坐标零点在图像左下角。 好了,有了以上的知识后,可以开始压缩程序啦恩,现在问题是怎么读取那两个结构体,大家都知道c语言的文件读取有两种方式,一个是按字节读取,一个是按位读取,那么用哪个呢?只好又百度了.好了,百度好了:二进制方式很简单,读文件时,会原封不动的读出文件的全部內容,写的時候,也是把內存缓冲区的內容原封不动的写到文件中。而文本方式就不一样了,在读文件时,会将换行符号CRLF(0 x0D 0 x0A)全部转换成单个的0 x0A,并且当遇到结束符CTRLZ(0 x1A)时,

11、就认为文件已经结束。相应的,写文件时,会将所有的0 x0A换成0 x0D0 x0A。 所以,若使用文本方式打开二进制文件时,就很容易出现文件读不完整,或內容不对的错误。即使是用文本方式打开文本文件,也要谨慎使用,比如复制文件,就不应该使用文本方式。(更多类容请自己百度)这里我们选择二进制的读入方式fread(&h, sizeof(BITMAPFILEHEADER), 1, fp);以上以读取头部为例,其它的类似(注意要按顺序读取,不要弄反了)好了,到此第一步已经完成了我们把灰度信息读取到了一个数组里,那开始压缩了,这个按书上那个程序就好,压完得到一个l,b,分别记录每一段的长度和那一段每个像素

12、需要的位数,特别提醒,书上的程序有一个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应该取第j段中占用位数最大的那个 max = le(pi); bj = max; bj不应该简单的赋值为bsj;而是应该取第j段中占用位数最大的那个。(太相信课本了,以至于开始的时候没仔细看它,浪费了好几个小时())另外要注意书上那个程序灰度值数组下标是从1开始的,这点要注意下(这个地方也死了1

13、个多小时())好了现在我们得到两个数组l和b,分别记录每一段的长度和那一段每个像素需要的位数,好了开始将数据压缩到文件里吧,恩怎么压呢?当然是一段一段压啦,首先放入第一段的长度(8位)然后是每个像素所占的位数(3位)然后循环一下把这个段压下去,这些都不是8的倍数该怎么压呀猛一想确实挺头疼的。仔细想想我们只要用一个变量j指示当前字节下标(也就是你要压缩到的那个数组),然后一个变量left指示当前字节还剩多少位可用这样比如说将一个占bit位的数据压入的代码是if (left (bit - left); aj +=(temp (8 + left - bit) & 0 xff; left = 8 -

14、(bit - left); else aj += (temp (left - bit) & 0 xff; left -= bit; 这个自己在草稿纸演算一下应该没问题的,另外一个特别注意的地方,每次向左移位之后都要与oxff做个按位与操作,以获取低8位的数据,即使你声明temp是unsigned char型也一样,因为在表达式中 char unsigned char short 都会被提升为int型,这叫做整型提升(我也不知道为什么,可能是便于运算看,这里又纠结了1个多小时,泪奔.)好了,灰度值我们知道怎么压缩了,下面就开始压缩吧,头部,头部信息,调试板不变,直接写进文件,额 不对,我们怎么知

15、道压缩后的灰度值数组有多长? 还记得这个吗DWORD biSizeImage; 实际的位图数据占用的字节数,我们把压缩后的灰度值数组大小存进去,到时候解压的时候别忘了恢复哈。压缩完了,下面开始解压,解压就是压缩的逆过程同样的,我们用i指示当前要读取的字节,left指示当前字节剩余的可用位数,bit表示要读取的灰度值的位数,将结果存到temp里面 if (left = bit) temp += (ai+ (8 - bit); temp += ai (8 - (bit - left); left = 8 - (bit - left); else temp += (ai (left - bit) (

16、8 - bit); left -= bit; 对运算符的优先级不了解的话还是老老实实加括号吧(同样的自己在草稿纸上演算一下)至此,大功告成了。将bmp四部分的信息按顺序写入文件就好了。下面贴上可以运行的代码,写得有点乱,有时间重构下.压缩程序#include #include #include #include #define lmax 256 /压缩时每一段包含的最大像素数#define header 11 /每一段需要11位来保存这一段的信息(8位用来表示段长度 /,3位用来表示每一像素占的位数)int bmpWidth; /图像的宽int bmpHeight; /图像的高RGBQUAD

17、*pColorTable; /颜色表指针int biBitCount; /图像类型,每像素位数unsigned char *pBmpBuf; /灰度值数组int lineByte; /每一行的字节数BITMAPFILEHEADER h; /头部BITMAPINFOHEADER head; /头部信息int i, j; / 循环要用到的遍历变量int *s, *l, *b; /这三个数组的意义和书上是一样的int m; /分割的段数 int le(int i) /log(i+1)向上取整 int k = 1; i = i / 2; while (i 0) k+; i = i / 2; retur

18、n 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 = 1; i = n; i+) bi = le(pi); bmax = bi; si = si - 1 + bmax; li = 1; for (j =

19、 2; j = i & j = lmax; j+) 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; int n = bmpWidth * bmpHeight; traceback(n, s, l); sm = n; for (j = 1; j = m; j+) lj = lsj; max = -1; for (i = sj - 1 + 1; i max) /bi应该取第i段中占

20、用位数最大的那个 max = le(pi); bj = max; void store(unsigned char *a, int *j, int *left, int bit, int temp)/将temp存到数组里 if (*left (bit - (*left); a(*j) +=(temp (8 + (*left) - bit) & 0 xff; *left = 8 - (bit - (*left); else a(*j) += (temp (*left - bit) & 0 xff; *left -= bit; void compressToFile(char*file,unsig

21、ned char p, int l, int b, int m) /压缩存到文件里 FILE *out = 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; for (i = 1; i = m; i+) store(a,&j,&left,8,li - 1); store(a,&j,&left,3,bi - 1); t = k + li; for (; k t;

22、 k+) store(a,&j,&left,bi,pk); head.biSizeImage = j + 1; /修改文件大小,这个解压图像中要用到 fwrite(&h, sizeof(BITMAPFILEHEADER), 1, out); fwrite(&head, sizeof(BITMAPINFOHEADER), 1, out); fwrite(pColorTable, sizeof(RGBQUAD), 256, out); fwrite(a, 1, j + 1, out); fclose(out); unsigned char* readBmp(char *file) /读取文件 un

23、signed char*pBmpBuf; FILE *fp = fopen(file, rb); fread(&h, sizeof(BITMAPFILEHEADER), 1, fp); /获取图片头信息 fread(&head, sizeof(BITMAPINFOHEADER), 1, fp); /定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中 bmpWidth = head.biWidth; /获取图像宽、高、每像素所占位数等信息 bmpHeight = head.biHeight; biBitCount = head.biBitCount; lineByte = (bm

24、pWidth * biBitCount / 8 + 3) / 4 * 4;/定义变量,计算图像每行像素所占的字节数(必须是4的倍数) if (biBitCount = 8) pColorTable = (RGBQUAD*) malloc(sizeof(RGBQUAD) * 256);/申请颜色表所需要的空间,读颜色表进内存 fread(pColorTable, sizeof(RGBQUAD), 256, fp); /灰度图像有颜色表,且颜色表表项为256 pBmpBuf = (unsigned char*) malloc(sizeof(unsigned char) * (lineByte /申

25、请位图数据所需要的空间,读位图数据进内存 * bmpHeight + 1); fread(pBmpBuf + 1, 1, lineByte * bmpHeight, fp); /为了数组下标从1开始 fclose(fp); /关闭文件 return pBmpBuf; int main() char *file = input.bmp; pBmpBuf = readBmp(file); s = (int*) malloc(sizeof(int) * (bmpWidth * bmpHeight + 1); l = (int*) malloc(sizeof(int) * (bmpWidth * bm

26、pHeight + 1); b = (int*) malloc(sizeof(int) * (bmpWidth * bmpHeight + 1); compress(pBmpBuf, s, l, b); collect(pBmpBuf, s, l, b); compressToFile(sqh.bmp,pBmpBuf, l, b, m); return 0;解压程序#include #include #include #include int bmpWidth; /图像的宽int bmpHeight; /图像的高RGBQUAD *pColorTable; /颜色表指针int biBitCoun

27、t; /图像类型,每像素位数unsigned char *pBmpBuf; /要恢复灰度值数组int lineByte; /每一行的字节数BITMAPFILEHEADER h; /头部BITMAPINFOHEADER head; /头部信息unsigned char *a; /压缩的灰度值数组 void load(unsigned char *a, int *i, int *left, int bit, int *temp)/将temp存到数组里 if (*left = bit) *temp += (a(*i)+ (8 - bit); *temp += a(*i) (8 - (bit - (*

28、left); *left = 8 - (bit - (*left); else *temp += (a(*i) (*left) - bit) (8 - bit); *left -= bit; void craming() FILE *ss = fopen(restore.bmp, wb); int left = 8, bit, length, k; int i = 0;/解压前数组index int j = 0;/解压后数组index while (i head.biSizeImage) length = 0; bit = 0; load(a, &i, &left, 8, &length); load(a, &i, &left, 3, &bit); length+; /因为压缩的时候有减1 bit+; k = j; for (; j k + length; j+) load(a, &i, &left, bit, &pBmpBufj); head.biSizeImage = bmpWidth * bmpHeight; /恢复像素所占的空间 fwrite(&h, sizeof(BITMAPFILEHEADER), 1, ss); fwrite(&head, sizeof(BITMAPINFOHEADER), 1, ss); fwrite(pColorTable, si

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论