C语言读取BMP文件头、信息头及像素信息
来源:互联网 发布:淘宝卖家管理流程图 编辑:程序博客网 时间:2024/05/16 08:25
本学期的实验之一,看网上许多文章关于读取像素的处理并不是很完整,就放上来一个。
/***************************************************** * usage:<BMP file><r output><g output><b output><copy file> *****************************************************/#include <stdio.h>#include <stdlib.h>typedef unsigned short int WORD;typedef unsigned int DWORD;typedef unsigned char BYTE;typedef struct tagBITMAPFILEHEADER{ WORD bfType; // 位图文件的类型,必须为BM(0-1字节) DWORD bfSize; // 位图文件的大小,以字节为单位(2-5字节) WORD bfReserved1; // 位图文件保留字,必须为0(6-7字节) WORD bfReserved2; // 位图文件保留字,必须为0(8-9字节) DWORD bfOffBits; // 位图数据的起始位置(10-13字节),以相对于 // 位图文件头的偏移量表示,以字节为单位} BITMAPFILEHEADER;typedef struct tagBITMAPINFOHEADER{ DWORD biSize; // 本结构所占用字节数(14-17字节) DWORD biWidth; // 位图的宽度,以像素为单位(18-21字节) DWORD biHeight; // 位图的高度,以像素为单位(22-25字节) WORD biPlanes; // 目标设备的级别,必须为1(26-27字节) WORD biBitCount; // 每个像素所需的位数,必须是1(双色),(28-29字节) // 4(16色),8(256色)或24(真彩色)之一 DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),(30-33字节) // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一 DWORD biSizeImage; // 位图的大小,以字节为单位(34-37字节) DWORD biXPelsPerMeter; // 位图水平分辨率,每米像素数(38-41字节) DWORD biYPelsPerMeter; // 位图垂直分辨率,每米像素数(42-45字节) DWORD biClrUsed; // 位图实际使用的颜色表中的颜色数(46-49字节) 设为0的话,则说明使用所有调色板项,即2 biBitCount种颜色 DWORD biClrImportant; // 位图显示过程中重要的颜色数(50-53字节)}BITMAPINFOHEADER;typedef struct tagRGBQUAD{BYTE rgbBlue; // 蓝色的亮度(值范围为0-255)BYTE rgbGreen; // 绿色的亮度(值范围为0-255)BYTE rgbRed; // 红色的亮度(值范围为0-255)BYTE rgbReserved; // 保留字,必须为0} RGBQUAD;int main(int argc, char** argv){ FILE *result_r;// 输出的三个通道文件 FILE *result_g; FILE *result_b;FILE *bmp;// 要读入的BMP文件 FILE *copy;// 要复制的BMP文件 long real_w;// 一个扫描行的实际占用字节数 long offset = 0;// 用于偏移每行结尾处的冗余数据 BITMAPFILEHEADER header;// BMP文件头BITMAPINFOHEADER info;// BMP信息头 if (argc!=6) { printf("usage:<BMP file><r file><g file><b file><copy file>"); return -1; } if ((bmp=fopen(argv[1], "r"))==NULL) { printf("the file of BMP isn't exist\n"); return -1; } copy=fopen(argv[5],"w"); fread(&header,14,1,bmp); fwrite(&header, 14, 1, copy); fread(&info,40,1,bmp); fwrite(&info, 40, 1, copy); printf("宽:%d 高:%d\n",info.biWidth,info.biHeight);printf("每个像素所需的位数: %d\n", info.biBitCount);printf("位图压缩类型: %d\n", info.biCompression);printf("位图的总颜色数: %d\n", info.biClrUsed); result_r=fopen(argv[2],"w"); result_g=fopen(argv[3],"w"); result_b=fopen(argv[4],"w"); real_w=(info.biWidth*info.biBitCount+31)/32*4; offset=real_w-(info.biWidth * info.biBitCount+7) / 8; if(info.biBitCount==24) // 真彩色{int i=0; for (;i<info.biHeight;i++){ int j=0; for (; j<info.biWidth; j++) { RGBQUAD rgb; char string[16]; fread(&rgb.rgbBlue,1,1,bmp); fwrite(&rgb.rgbBlue, 1, 1, copy); fread(&rgb.rgbGreen,1,1,bmp); fwrite(&rgb.rgbGreen, 1, 1, copy); fread(&rgb.rgbRed,1,1,bmp); fwrite(&rgb.rgbRed, 1, 1, copy); rgb.rgbReserved=0; sprintf(string, "%d ", rgb.rgbRed); fputs(string,result_r); sprintf(string, "%d ", rgb.rgbGreen); fputs(string,result_g); sprintf(string, "%d ", rgb.rgbBlue); fputs(string,result_b); } if(offset&&j!=info.biWidth-1) { fseek(bmp, offset, SEEK_CUR); } fputs("\n", result_r); fputs("\n", result_g); fputs("\n", result_b);}} else { // 索引颜色 int i=0; int index=0; int biClrUsed; RGBQUAD *clTable;// 用于存储颜色表 biClrUsed=1<<info.biBitCount; clTable = (RGBQUAD *)malloc(biClrUsed*sizeof(RGBQUAD)); for(;i<biClrUsed;i++){// 获取颜色表 fread(&clTable[i], 4, 1, bmp); fwrite(&clTable[i], 4, 1, copy); clTable[i].rgbReserved=0; } i=0; for (;i<info.biHeight;i++) { int j=0; WORD tmp; for (; j<info.biWidth; j++) { RGBQUAD rgb; char string[16]; if (info.biBitCount==8) {// 8位,一个一个地取索引 fread(&tmp,1,1,bmp); fwrite(&tmp, 1, 1, copy); index = tmp; } else if (info.biBitCount==4) {// 4位,每两次循环取一次索引 if (j%2) { index = tmp&0xF; } else { fread(&tmp, 1, 1, bmp); fwrite(&tmp, 1, 1, copy); index = tmp>>4; } } else if (info.biBitCount==1) {// 1位,每8次循环取一次索引 int joffset = j%8; if (!joffset){ fread(&tmp, 1, 1, bmp); fwrite(&tmp, 1, 1, copy); } index = (tmp>>(7-joffset))&0x1; } rgb=clTable[index]; sprintf(string, "%d ", rgb.rgbRed); fputs(string,result_r); sprintf(string, "%d ", rgb.rgbGreen); fputs(string,result_g); sprintf(string, "%d ", rgb.rgbBlue); fputs(string,result_b); } if(offset) { int i=0; fseek(bmp, offset, SEEK_CUR); for (; i<offset; i++) { WORD zero=0; fwrite(&zero, 1, 1, copy); } } fputs("\n", result_r); fputs("\n", result_g); fputs("\n", result_b); } free(clTable); } fclose(result_r); fclose(result_g); fclose(result_b);fclose(bmp); fclose(copy); printf("解析完毕。\n");return 0; }
一些地方看不懂的可以看我的实验报告:
数字图像实验:BMP文件的读取
实验报告
实验内容
从指定路径读取一个BMP文件,提取其文件头和信息头,并根据图片的像素深度分不同情况来读出图片的像素数据。分三通道输出到三个文本文件中。同时,还创建了一个原BMP文件的拷贝,将原图片的文件头、信息头、颜色表、像素数据分块拷贝到新的文件中。
实验环境
OS:Mac OS X 10.8 Mountain Lion 64位
编译器:gcc 4.2 (Apple)
CPU:2.5GHz Intel Core i5
IDE:XCode 4.6
测试环境:XCode,Terminal
实验思路
使用纯C语言。首先,声明三个结构体,分别用于存储BMP文件的文件头、信息头和颜色表。使用fopen函数打开BMP文件,并使用fread函数将BMP的文件头、信息头分块读入相应结构体的变量header和info中。
由于BMP文件中,一个扫描行的字节数必须是4的倍数,在水平像素不足4的倍数时,多余空位用0补齐。我们在读取像素信息的时候不需要这些垃圾信息,因此这里先计算几个值用作以后处理垃圾信息的参数。
一个是long型变量real_w,该变量表示BMP文件一个扫描行实际占用的位数。它的计算公式是(info.biWidth*info.biBitCount+31)/32*4;
另一个是long型变量offset,该变量表示BMP文件一个扫描行的像素信息所占的位数与真实占用位数的差值,这个变量将在以后用于fseek的移位操作,计算式子是real_w-(info.biWidth * info.biBitCount+7)/8。
此后,通过读取info变量中的biBitCount成员来判断图片的像素深度,若为24位,则无颜色表,我们使用一个嵌套循环,外层是图片的高度(像素为单位),内层是图片的宽度(像素为单位),对于每一个像素,我们都用fread读取图片中3字节的数据并写进相应的三个输出文件中。在每一行读取完毕后,我们使用fseek,传入之前的offset变量,将文件位置指针偏移到下一行的行首,从而跳过每一行的垃圾数据。
若像素深度不为24位,则是索引颜色图片,我们先读取颜色表。索引颜色的数量使用1<<info.biBitCount算出(相当于2的info.biBitCount次方),并从图片中读取这么多的颜色数据(一个颜色占四个字节),用一个指针clTable指向这块数据。此后使用类似于24位中我们使用的嵌套循环,一个像素一个像素地读取,在内层循环中,我们定义一个整形变量index,用于存储当前像素所对应的颜色索引。
对于index的取值,我们分以下几种情况讨论:
1.若像素深度为8位,则1个字节1个像素,我们在每次循环中直接读取1个字节的数据并赋给index即可;
2.若像素深度为4位,则1个字节2个像素,我们用取余的方式j%2(j是内层循环的控制变量)判断其值,若为0,表示当前像素存储在一个字节的高4位字节处,我们先用fread取出该字节,再将该字节右移4位,这样就得到了其高字节4位数对应的整形值;若为1, 表示当前像素存储在一个字节的低4位字节处,我们直接将上次取得的字节与00001111B作与运算,这样就取出了这个字节的低4位。
3.若像素深度为1位,则1个字节1个像素,我们用类似2中的方式,对j取8的余数,仅当j%8为0时,我们才出图片中取出一个字节;对每次循环,我们将取出字节(设为tmp)作如下处理:index = (tmp>>(7-j%8))&0x1来依次取出tmp的每一位。
获得了索引index以后,只需在clTable中取出指定位置的颜色,输出到相应的文本文件中即可。
对于复制BMP文件的操作,我们只需要创建一个FILE指针copy,在每次从BMP中用fread取数据的地方,同样调用fwrite将前面取出的数据再写入copy中,此外,在按行读取像素时,由于我们使用了fseek偏移了文件位置指针,我们也要相应的向copy中写入相中位数的0(这里我们用一个小小的for循环实现)。
实验心得
BMP文件相对于WAV文件更加复杂,例如头文件内容更多,根据像素深度不同数据存储差异较大,也存在很多必须要关注的细节,如:
1.在24位BMP文件中,一个像素仅占用3个字节(对应颜色的B,G,R),不存储RGBQUARD的保留字,而在非24位的BMP文件的颜色表中,对于每个颜色却使用4个字节,这一差异在很多资料中都没有给出。
2.当像素深度为4或1时,1个字节中会存储多个像素,这里涉及了低层的位运算操作。此外,这种情况也必须注意偏移量offset的取整问题。
3.此程序还有一定的缺陷,因其不能读取非标准格式的BMP文件(例如,在PhotoShop中,若使用“局部”方式创建索引颜色,有可能使原文件的颜色表数不足2的N次方个。导致程序读取失败,要正确读取,一定要在转换成BMP时选择Window或Mac OS颜色表。
- C语言读取BMP文件头、信息头及像素信息
- 遍历文件目录及bmp位图信息头的读取
- 读取bmp图片的文件头,与位图信息头
- 读取 bmp 文件头信息, read bmp infomation
- BMP头文件格式以及C语言读取头文件
- 【转】BMP头文件格式以及C语言读取头文件
- BMP头文件格式以及C语言读取头文件
- BMP头文件格式以及C语言读取头文件(一)
- BMP头文件格式以及C语言读取头文件(二)
- BMP头文件格式以及C语言读取头文件(二)
- 读取头信息和响应头信息
- POI读取Word文件头信息
- 运用opencv 读取BMP图像像素信息 代码及实现
- c语言实现读取位图像素信息
- 读取bmp文件头出错
- MIME头文件信息
- caf文件头信息
- WAV文件头信息
- Debian安装firefox
- android坏境配置
- 数据挖掘中所需的概率论与数理统计知识
- ubutu 输入法
- ubutu root
- C语言读取BMP文件头、信息头及像素信息
- C#从Excel单元格中取值结果总是显示system._comobject
- 无线连接两台笔记本电脑
- URL重写的几种实现方式的总结
- 最新颜色代码
- IE中使用WinForm控件
- Oracle语法温故(块结构、变量命名、数据类型、关系运算符、逻辑运算符)
- 一只小鸟初学设计模式(一)决策者模式
- google protobuf java.lang.NoSuchFieldError: alwaysUseFieldBuilders问题