bmp文件的格式:
包括4部分。第一部分是文件头,占用14字节;第二部分是信息头,占用40字节;第三部分是调色板,这是可选的部分;第四部分是像素信息。
各部分的字段的详细信息可见下表:
bmp文件的结构体的定义:
#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef unsigned int UINT; //4 bytes
typedef unsigned short USHORT; //2 bytes
typedef signed int INT; //4 bytes
typedef unsigned char BYTE; //1 byte
//pragma用来取消默认对齐,如果没有,所占大小为16字节
#pragma pack(push)
#pragma pack(1)
//文件头
typedef struct BITMAPFILEHEADER{
USHORT bFileType;//文件类型,有固定值
UINT bFileSize;//文件的字节大小
USHORT bReserved1;//保留1
USHORT bReserved2;//保留2
UINT bPixelDataOffset;//文件头起始到位图数据之间的偏移量
}BITMAPFILEHEADER;
#pragma pack(pop)
//信息头
typedef struct BITMAPINFOHEADER{
UINT bHeaderSize;//信息头的字节大小,有固定值
INT bImageWidth;//图像的宽度(单位:像素)
INT bImageHeight;//图像的高度(单位:像素)
USHORT bPlanes;//色层
USHORT bBitsPerPixel;//像素位数
UINT bCompression;//图像压缩,有固定值
UINT bImageSize;//图像大小,有固定值
INT bXpixelsPerMeter;//x轴的每米像素数
INT bYpixelsPerMeter;//y轴的每米像素数
INT bTotalColors;//调色板里颜色数
UINT bImportantColors;//重要颜色数,ignored
}BITMAPINFOHEADER;
//RGB
typedef struct RGBDATA{
BYTE blue;
BYTE green;
BYTE red;
}RGBDATA;
//调色板
typedef struct RGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
}RGBQUAD;
接下来是方法:
#include "bmpModel.h"
#include <string.h>
//图像的宽度(像素)
int width = 0;
//图像的高度(像素)
int height = 0;
//打开bmp文件
FILE* openImage(char* filename, char* mode){
FILE* fp;
//准备文件的打开模式,给打开模式一个合适的值
if(strcmp(mode,"r") == 0){
mode="rb";
}else if(strcmp(mode,"w") == 0){
mode="ab";
}else{
fprintf(stderr,"文件打开模式%s使用错误",mode);
return 0;
}
//bmp文件打开
if((fp = fopen(filename,mode)) == 0){
fprintf(stderr,"文件打开%s失败",filename);
return 0;
}
return fp;
}
//关闭bmp文件
void closeImage(FILE* fp){
fclose(fp);//关闭文件
free(fp);//释放指针
}
//读取bmp文件的文件头
BITMAPFILEHEADER* readFileHeader(FILE* fp){
BITMAPFILEHEADER* fileHead = (BITMAPFILEHEADER*)malloc(sizeof(BITMAPFILEHEADER));
//错误处理
if(fileHead == NULL){
fprintf(stderr,"文件头内存分配失败");
}
if(fread(fileHead,sizeof(BITMAPFILEHEADER),1,fp) != 1){
fprintf(stderr,"文件头读取失败");
}
return fileHead;
}
//读取bmp文件的信息头
BITMAPINFOHEADER* readInfoHeader(FILE* fp){
BITMAPINFOHEADER* infoHead = (BITMAPINFOHEADER*)malloc(sizeof(BITMAPINFOHEADER));
//错误处理
if(infoHead == NULL) {
fprintf(stderr,"信息头内存分配失败");
}
if(fread(infoHead,sizeof(BITMAPINFOHEADER),1,fp) != 1){
fprintf(stderr,"信息头读取失败");
}
return infoHead;
}
// 读取bmp文件的像素信息,创建二维数组,并存入二维数组
RGBDATA** readBmpData(FILE *fp){
int i = 0;
int j = 0;
RGBDATA** data;//存储图像像素信息的二维数组
BYTE* temp;//一个字节,用于放图像对齐的填充数据
//读取文件头
BITMAPFILEHEADER* fileHead = readFileHeader(fp);
//读取信息头
BITMAPINFOHEADER* infoHead = readInfoHeader(fp);
printf("fileHeader size=%d\n",sizeof(BITMAPFILEHEADER));
printf("infoHeader size=%d\n",sizeof(BITMAPINFOHEADER));
//printf("RGBDATA size=%d",sizeof(RGBDATA));
//获取图像的宽高
width = infoHead->bImageWidth;
height = infoHead->bImageHeight;
//给二维数组分配内存
data = (RGBDATA**)malloc(sizeof(RGBDATA*)*height);
temp = (BYTE*)malloc(sizeof(BYTE));
if(data ==NULL){
fprintf(stderr,"像素内存分配失败");
return 0;
}
int k = 0;
for(; k < height; k++){
data[k] = (RGBDATA*)malloc(sizeof(RGBDATA)*width);
if(data[k] == NULL){
fprintf(stderr,"像素内存分配失败");
return 0;
}
}
//如果有调色板,读取它
if(fileHead->bPixelDataOffset > 54){
RGBQUAD* quad = (RGBQUAD*)malloc(sizeof(RGBQUAD));
if(quad == NULL){
fprintf(stderr,"调色板内存分配失败");
}
if(fread(quad,sizeof(RGBQUAD),1,fp) != 1){
fprintf(stderr,"调色板读取失败");
}
}
//把像素数据存储到二维数组中
for(;i < height; i++){
for(j = 0; j < width; j++){
fread(&data[i][j],sizeof(RGBDATA),1,fp);
}
//处理对齐问题。
//bmp文件存储要求每行字节数必须是4的倍数
//这里把超过width的且不是4的倍数的数据取出
int num = j * 3;
while(num%4 != 0){
fread(temp,sizeof(BYTE),1,fp);
num++;
}
}
return data;
}
int main(){
FILE* fp;
//bmp文件打开
fp = openImage("bmp1.bmp","r");
//读取像素信息,存入二维数组arr
RGBDATA** arr = readBmpData(fp);
int i = 0;
int j = 0;
printf("\n");
printf("以下为从上到下的RGB顺序:\n");
//打印,调整了展示顺序
for(i = height - 1; i >= 0 ; i--){
for(j = 0; j < width; j++){
printf("[%3d,%3d,%3d]\t",arr[i][j].red,arr[i][j].green,arr[i][j].blue);
}
printf("\n");
}
//关闭文件
closeImage(fp);
return 0;
}
注意点:
1.需要用#pragma取消结构体的默认对齐,否则结构体不是规定的14字节。
2.判断是否有调色板,使用BITMAPFILEHEADER.bPixelDataOffset参数,这个参数表示从文件信息开头到pixel data区的位移,如果没有调色板,则该值为固定的14+40=54字节。
3.bmp文件存储格式要求每行的字节数必须是4的倍数。这被称为dword对齐(dword是一种数据类型,长度为4个字节)。如果图像的一行字节数不能被4整除,就需要在每行的末尾补齐0以达到规定。因此需要处理掉可能补0的地方:在存入二维数组时,对于超过图像宽度的部分,如果不能整除4,指针就再移动一个字节,直到可以整除4为止。
4.输出需要注意:
文件的颜色格式为b,g,r的顺序,不是常说的RGB顺序。
图像的像素信息是从左至右,从下到上存储的,二维数组中最后一行对应图像的第一行……