c99标准的bmp格式文件读入
bmp简介
BMP取自位图Bitmap的缩写,也称为DIB(与设备无关的位图),是一种独立于显示器的位图数字图像文件格式。常见于微软视窗和OS/2操作系统,Windows GDI API内部使用的DIB数据结构与 BMP 文件格式几乎相同。
图像通常保存的颜色深度有2(1位)、16(4位)、256(8位)、65536(16位)和1670万(24位)种颜色(其中位是表示每点所用的数据位)。8位图像可以是索引彩色图像外,也可以是灰阶图像。表示透明的alpha通道也可以保存在一个类似于灰阶图像的独立文件中。带有集成的alpha通道的32位版本已经随着Windows XP出现,它在Windows系统的登录界面和系统主题中都有使用。
节选自:维基百科
实现效果
将一个bmp文件的数据放到Image结构中,其中包含通道数(channels),宽度(width)、高度(height)、图像数据,图像数据按照三通道b、g、r顺序排列,每个通道中第一个维度为height, 第二个维度为width。
实现
首先是头文件代码(其实应该把实现放到.c中但是懒得搞了😰)
#ifndef BMP_H
#define BMP_H
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef struct {
unsigned long bfSize; // 文件大小
unsigned short bfReserved1; // 保留,必须设置为0(6-7字节)
unsigned short bfReserved2; // 保留,必须设置为0(8-9字节)
unsigned long bfOffBits; // 从文件头到像素数据的偏移
} BitMapFileHeader; // 文件信息头结构体
typedef struct {
unsigned long biSize; // 此结构体的大小(14-17字节)
long biWidth; // 图像的宽(18-21字节)
long biHeight; // 图像的高(22-25字节)
unsigned short biPlanes; // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1(26-27字节)
unsigned short biBitCount; // 一像素所占的位数,一般为24 (28-29字节)
unsigned long biCompression; // 说明图象数据压缩的类型,0为不压缩。 (30-33字节)
unsigned long biSizeImage; // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits (34-37字节)
long biXPelsPerMeter; // 说明水平分辨率,用象素/米表示。一般为0 (38-41字节)
long biYPelsPerMeter; // 说明垂直分辨率,用象素/米表示。一般为0 (42-45字节)
unsigned long biClrUsed; // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。 (46-49字节)
unsigned long biClrImportant; // 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。(50-53字节)
} BitMapInfoHeader;
typedef struct {
unsigned char rgbBlue; //该颜色的蓝色分量
unsigned char rgbGreen; //该颜色的绿色分量
unsigned char rgbRed; //该颜色的红色分量
unsigned char rgbReserved; //保留值,必须为0
} RgbQuad;
typedef struct {
int width;
int height;
int channels;
unsigned char*** imageData;
// channel height width
}Image;
/*
初始化Image的imageData数据
*/
unsigned char*** initialImageData(int channel, int height, int width) {
unsigned char*** imageData;
imageData = (unsigned char***)calloc(channel, sizeof(int**));
for (int i = 0; i < channel; i++) {
imageData[i] = (unsigned char**)calloc(height, sizeof(int*));
}
for (int i = 0; i < channel; i++) {
for (int j = 0; j < height; j++) {
imageData[i][j] = (unsigned char*)calloc(width, sizeof(int));
}
}
return imageData;
}
Image* bmpRead(const char* filePath) {
Image* bmpImg;
FILE* pFile;
unsigned short fileType;
BitMapFileHeader bmpFileHeader;
BitMapInfoHeader bmpInfoHeader;
int channels = 1;
int width = 0;
int height = 0;
int step = 0;
int offset = 0;
unsigned char pixVal;
RgbQuad* quad;
int i, j, k;
bmpImg = (Image*)malloc(sizeof(Image));
pFile = fopen(filePath, "rb");
if (!pFile) {
free(bmpImg);
return NULL;
}
fread(&fileType, sizeof(unsigned short), 1, pFile);
if (fileType == 0x4D42) {
fread(&bmpFileHeader, sizeof(BitMapFileHeader), 1, pFile);
fread(&bmpInfoHeader, sizeof(BitMapInfoHeader), 1, pFile);
if (bmpInfoHeader.biBitCount == 8) {
channels = 1;
width = bmpInfoHeader.biWidth;
height = bmpInfoHeader.biHeight;
offset = (channels * width) % 4;
if (offset != 0) {
offset = 4 - offset;
}
bmpImg->width = width;
bmpImg->height = height;
bmpImg->channels = 1;
bmpImg->imageData = initialImageData(bmpImg->channels, bmpImg->height, bmpImg->width);
quad = (RgbQuad*)malloc(sizeof(RgbQuad) * 256);
fread(quad, sizeof(RgbQuad), 256, pFile);
free(quad);
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
fread(&pixVal, sizeof(unsigned char), 1, pFile);
bmpImg->imageData[0][i][j] = pixVal;
}
if (offset != 0) {
for (j = 0; j < offset; j++) {
fread(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
else if (bmpInfoHeader.biBitCount == 24) {
channels = 3;
width = bmpInfoHeader.biWidth;
height = bmpInfoHeader.biHeight;
bmpImg->width = width;
bmpImg->height = height;
bmpImg->channels = 3;
bmpImg->imageData = initialImageData(bmpImg->channels, bmpImg->height, bmpImg->width);
step = channels * width;
offset = (channels * width) % 4;
if (offset != 0) {
offset = 4 - offset;
}
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
for (k = 0; k < channels; k++){
fread(&pixVal, sizeof(unsigned char), 1, pFile);
bmpImg->imageData[k][i][j] = pixVal;
}
if (offset != 0) {
for (j = 0; j < offset; j++) {
fread(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
}
}
fclose(pFile);
free(pFile);
return bmpImg;
}
bool bmpWrite(const char* filePath, Image* image) {
FILE* pFile;
unsigned short fileType;
BitMapFileHeader bmpFileHeader;
BitMapInfoHeader bmpInfoHeader;
int step;
int offset;
unsigned char pixVal = '\0';
int i, j;
RgbQuad* quad;
pFile = fopen(filePath, "wb");
if (!pFile) {
return false;
}
fileType = 0x4D42;
fwrite(&fileType, sizeof(unsigned short), 1, pFile);
if (image->channels == 3) { // 24位,通道,彩图
step = image->channels * image->width;
offset = step % 4;
if (offset != 4) {
step += 4 - offset;
}
bmpFileHeader.bfSize = image->height * step + 54;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfOffBits = 54;
fwrite(&bmpFileHeader, sizeof(BitMapFileHeader), 1, pFile);
bmpInfoHeader.biSize = 40;
bmpInfoHeader.biWidth = image->width;
bmpInfoHeader.biHeight = image->height;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 24;
bmpInfoHeader.biCompression = 0;
bmpInfoHeader.biSizeImage = image->height * step;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biClrImportant = 0;
fwrite(&bmpInfoHeader, sizeof(BitMapInfoHeader), 1, pFile);
for (i = 0; i < image->height; i++) {
for (j = 0; j < image->width; j++) {
for (int k = 0; k < image->channels; k++) {
pixVal = image->imageData[k][i][j];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
if (offset != 0) {
for (j = 0; j < 4 - offset; j++) {
pixVal = 0;
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
}
else if (image->channels == 1) { // 8位,单通道,灰度图
step = image->width;
offset = step % 4;
if (offset != 4) {
step += 4 - offset;
}
bmpFileHeader.bfSize = 54 + 256 * 4 + image->width;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfOffBits = 54 + 256 * 4;
fwrite(&bmpFileHeader, sizeof(BitMapFileHeader), 1, pFile);
bmpInfoHeader.biSize = 40;
bmpInfoHeader.biWidth = image->width;
bmpInfoHeader.biHeight = image->height;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 8;
bmpInfoHeader.biCompression = 0;
bmpInfoHeader.biSizeImage = image->height * step;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 256;
bmpInfoHeader.biClrImportant = 256;
fwrite(&bmpInfoHeader, sizeof(BitMapInfoHeader), 1, pFile);
quad = (RgbQuad*)malloc(sizeof(RgbQuad) * 256);
for (i = 0; i < 256; i++) {
quad[i].rgbBlue = i;
quad[i].rgbGreen = i;
quad[i].rgbRed = i;
quad[i].rgbReserved = 0;
}
fwrite(quad, sizeof(RgbQuad), 256, pFile);
free(quad);
for (i = 0; i < image->height; i++) {
for (j = 0; j < image->width; j++) {
pixVal = image->imageData[0][i][j];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
if (offset != 0) {
for (j = 0; j < 4 - offset; j++) {
pixVal = 0;
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
fclose(pFile);
free(pFile);
return true;
}
#endif
代码使用
#include "bmp.h"
#include <stdio.h>
int main() {
const char* sourceImagePath = "in.bmp";
const char* targetImagePath = "out.bmp";
Image* img = bmpRead(sourceImagePath);
for (int k = 0; k < img->channels; k++) { // 三个通道分别为b、g、r
for (int j = 0; j < img->height; j++) {
for (int i = 0; i < img->width; i++) {
printf("%d\t", img->imageData[k][j][i]);
}
printf("\n");
}
printf("\n");
}
bmpWrite(targetPath, img);
return 0;
}