BMP
BMP
BMP文件:
bmp文件主要包含4个部分,分别为文件头,信息头,调色板(大多数没有),图像数据。
一、文件头
文件头描述了该文件的信息,定义如下:
#pragma pack (push, 1)
struct _file_header {
unsigned short fileType;
unsigned int fileSize;
unsigned short fileReserved1;
unsigned short fileReserved2;
unsigned int offset;
}; //14个字节
#pragma pack(pop)
结构体的布局必须是紧凑的,也就是不需要做对齐。各字段含义如下:
-
fileType
: BMP文件的标识,一般就是'B', 'M'两个字符,对应16进制为'0x4d42',vFileHeader->fileType = 0x4d42;
-
fileSize
: 整个文件的大小,4个部分的Byte数总和 -
fileReserved1
: 保留字段 -
fileReserved2
: 保留字段 -
offset
: 图像数据的偏移位置,通过fseek到图像数据位置再读取数据
二、信息头
信息头描述了图像的一些基本信息,如下:
struct _info_header {
unsigned int infoSize;
unsigned int imageWidth;
int imageHeight;
unsigned short imagePlane;
unsigned short imageCount;
unsigned int imageCompression;
unsigned int imageSize;
unsigned int hResolution;
unsigned int vResolution;
unsigned int clrUsed;
unsigned int clrImportant;
}; //40个字节
- infoSize: 信息头大小,
vInfoHeader->infoSize = sizeof(struct _info_header);
- imageWidth: 图像的宽
- imageHeight: 图像的高,注意这里是int,有正负之分。如是>0,表示各行的数据是逆向的(第1行数据存放在最后一行);如果<0,表示各行数据是顺序存放的
- imagePlane: 颜色平面数,总设为1
- imageCount: 一个像素所占比特数,可以设为8(灰度), 16, 24(RGB), 32(RGBA)
- imageCompression: 图像压缩类型,可以设为0,不压缩
- imageSize: 图像数据大小(字节)
- hResolution: 水平分辨率, 像素/米,可设为0
- vResolution: 垂直分辨率, 像素/米,可设为0
- clrUsed: 说明位图使用的颜色索引数
- clrImportant: 表示对图像显示有重要影响的颜色索引数目
三、图像数据
1. RGB顺序
注意是BGR存放。如果包含alpha通道,存放顺序是BGRA。
2. 每行数据需要做对齐:
每行的图像数据需要做4字节对齐。一行的字节数可以通过如下公式计算:
int line_size = (width * channel + 3) / 4 * 4;
假如width为301, channel为3, 实际数据大小为301*3 = 903
字节,需要数据大小(301 * 3 + 3) / 4 * 4 = 904
字节。
3. 图像数据的存放顺序:
-
顺序存放, 要求height为负数,imageHeight = -480
第1行数据
第2行数据
.
.
.
第n-1行数据
第n行数据 -
逆序存放, 要求height为正数,imageHeight = 480
第n行数据
第n-1行数据
.
.
.
第2行数据
第1行数据
四、例子
保存bmp文件:
#pragma pack(1)
struct _file_header {
unsigned short fileType;
unsigned int fileSize;
unsigned short fileReserved1;
unsigned short fileReserved2;
unsigned int offset;
};
#pragma pack(4)
struct _info_header {
unsigned int infoSize;
unsigned int imageWidth;
int imageHeight;
unsigned short imagePlane;
unsigned short imageCount;
unsigned int imageCompression;
unsigned int imageSize;
unsigned int hResolution;
unsigned int vResolution;
unsigned int clrUsed;
unsigned int clrImportant;
};
static int writeBMP(FILE *desFile, unsigned char *inputBuf,
unsigned int width, unsigned int height, int cn)
{
if (cn != 1 && cn != 4) {
printf("only support channel 1 and 4\n");
return -1;
}
if (cn == 1) {
struct _file_header *vFileHeader = NULL;
struct _info_header *vInfoHeader = NULL;
unsigned char *palleteData = NULL;
unsigned char *imageData = NULL;
unsigned char *bmpBuffer = NULL;
int w = (width + 3) / 4 * 4;
unsigned int imgSize = w * height;
unsigned int headerSize = 1024 + sizeof(struct _file_header)
+ sizeof(struct _info_header);
unsigned int fileSize = headerSize + imgSize;
unsigned int i, val, length = 1 << 8;
bmpBuffer = (unsigned char *)malloc(fileSize);
if (!bmpBuffer) {
printf("invalid buffer\n");
return -1;
}
vFileHeader = (struct _file_header *)bmpBuffer;
vInfoHeader = (struct _info_header *)(bmpBuffer
+ sizeof(struct _file_header));
palleteData = bmpBuffer + sizeof(struct _file_header)
+ sizeof(struct _info_header);
imageData = bmpBuffer + headerSize;
// assign vFileHeader & vInfoHeader
vFileHeader->fileType = 0x4d42;
vFileHeader->fileSize = (unsigned int)fileSize;
vFileHeader->fileReserved1 = 0;
vFileHeader->fileReserved2 = 0;
vFileHeader->offset = headerSize;
//vFileHeader->fileOffset1 = (unsigned short)
// ((headerSize << 16) >> 16);
//vFileHeader->fileOffset2 = (unsigned short)(headerSize >> 16);
vInfoHeader->infoSize = sizeof(struct _info_header);
vInfoHeader->imageWidth = width;
vInfoHeader->imageHeight = -(int)height;
vInfoHeader->imagePlane = 1;
vInfoHeader->imageCount = 8;
vInfoHeader->imageCompression = 0;
vInfoHeader->imageSize = 0;
vInfoHeader->hResolution = 0;
vInfoHeader->vResolution = 0;
vInfoHeader->clrUsed = 0;
vInfoHeader->clrImportant = 0;
for (i = 0; i < length; i++) {
val = (i * 255/(length - 1)) ^ 0;
palleteData[0] = (unsigned char)val;
palleteData[1] = (unsigned char)val;
palleteData[2] = (unsigned char)val;
palleteData[3] = (unsigned char)0;
palleteData += 4;
}
for (i = 0; i < height; ++i)
memcpy(imageData + i * w, inputBuf + width * i, width);
// write desFile
if (desFile == NULL) {
if (bmpBuffer != NULL) {
free(bmpBuffer);
bmpBuffer = NULL;
}
printf("desFile is NULL\n");
return -1;
}
fwrite(bmpBuffer, fileSize, 1, desFile);
if (bmpBuffer != NULL) {
free(bmpBuffer);
bmpBuffer = NULL;
}
} else if (cn == 4) {
struct _file_header *vFileHeader = NULL;
struct _info_header *vInfoHeader = NULL;
unsigned char *imageData = NULL;
unsigned char *bmpBuffer = NULL;
unsigned int imgSize = width * height;
unsigned int fileSize = sizeof(struct _file_header)
+ sizeof(struct _info_header) + imgSize * 4;
bmpBuffer = (unsigned char *)malloc(fileSize);
if (bmpBuffer == NULL) {
printf("malloc bmpBuffer failed\n");
return -1;
}
vFileHeader = (struct _file_header *)bmpBuffer;
vInfoHeader = (struct _info_header *)(bmpBuffer
+ sizeof(struct _file_header));
imageData = bmpBuffer + sizeof(struct _file_header)
+ sizeof(struct _info_header);
// assign vFileHeader & vInfoHeader
vFileHeader->fileType = 0x4d42;
vFileHeader->fileSize = (unsigned int)fileSize;
vFileHeader->fileReserved1 = 0;
vFileHeader->fileReserved2 = 0;
vFileHeader->offset = sizeof(struct _file_header)
+ sizeof(struct _info_header);
vInfoHeader->infoSize = sizeof(struct _info_header);
vInfoHeader->imageWidth = width;
vInfoHeader->imageHeight = -((int)height);
vInfoHeader->imagePlane = 1;
vInfoHeader->imageCount = 32;
vInfoHeader->imageCompression = 0;
vInfoHeader->imageSize = imgSize * 4;
vInfoHeader->hResolution = 0;
vInfoHeader->vResolution = 0;
vInfoHeader->clrUsed = 0;
vInfoHeader->clrImportant = 0;
// assign image data
memcpy(imageData, inputBuf, imgSize * 4);
// write desFile
if (desFile == NULL) {
if (bmpBuffer != NULL) {
free(bmpBuffer);
bmpBuffer = NULL;
}
printf("desFile is NULL\n");
return -1;
}
fwrite(bmpBuffer, fileSize, 1, desFile);
if (bmpBuffer != NULL) {
free(bmpBuffer);
bmpBuffer = NULL;
}
}
return 0;
}
读bmp:
void *read_bmp_alloc(const char *path,
uint8_t *mem, int *bmpWidth, int *bmpHeight)
{
FILE *fp = fopen(path, "rb");
printf("fp : %p\n", fp);
printf("open %s\n", path);
struct _file_header fh;
struct _info_header ih;
int ret;
int w;
int h;
int c;
uint8_t *buf = NULL;
int i, j, k;
int j2;
int size;
ret = fread(&fh, 1, sizeof(fh), fp);
ret = fread(&ih, 1, sizeof(ih), fp);
w = *bmpWidth = ih.imageWidth;
h = *bmpHeight = abs(ih.imageHeight);
c = ih.imageCount/8;
if (c != 3 && c != 1 && c != 4) {
printf("only support rgb bmp!\n");
ret = -1;
goto exit;
}
size = get_file_size(path);
buf = (uint8_t *)malloc(size - fh.offset);
fseek(fp, fh.offset, SEEK_SET);
if (!buf) {
printf("out of memory!\n");
ret = -1;
goto exit;
}
fread(buf, 1, size - fh.offset, fp);
mem = (uint8_t *)malloc(w * h * 3);
if (!mem)
goto exit;
uint8_t *p = (uint8_t *)mem;
uint8_t *b = (uint8_t *)buf;
if (c == 1) {
int wid_size = (w + 3) / 4 * 4;
for (j = 0; j < h; ++j) {
j2 = j;
if (ih.imageHeight > 0)
j2 = h - 1 - j;
for (k = 0; k < w; ++k) {
for (int i = 0; i < 3; ++i) {
p[i * h * w + j2 * w + k] = b[k];
}
}
b += wid_size;
}
} else if (c == 3) {
int wid_size = (w * 3 + 3) / 4 * 4;
for (j = 0; j < h; ++j) {
j2 = j;
if (ih.imageHeight > 0)
j2 = h - 1 - j;
for (k = 0; k < w; ++k) {
for (int i = 0; i < 3; ++i) {
p[i * h * w + j2 * w + k]
= b[k * c + i];
}
}
b += wid_size;
}
} else if (c == 4) {
for (j = 0; j < h; ++j) {
j2 = j;
if (ih.imageHeight > 0)
j2 = h - 1 - j; //处理行顺序
for (k = 0; k < w; ++k) {
for (i = 0; i < 3; ++i) {
p[i * h * w + j2 * w + k]
= b[k * c + i];
}
}
b += w*4; //处理对齐
}
}
exit:
if (fp)
fclose(fp);
if (buf)
free(buf);
return p;
}