Bmp format bitmap file read two-dimensional array (C language)

BadApple play was originally intended to get hold of, but not content with simply reading text files and output, and finally turned into a study of how to use C language to read a bitmap file and a two-dimensional array to store the pixel information.

The first step is to figure out the natural bmp file format. In various bitmap format, bmp since the data blocks are often not compressed, each pixel is composed of several independent groups of bits to represent or, reading and writing are relatively simple, only need to read a binary file according to the format required by fread on the line.

bmp file about four parts, the first part is the type of data file, stored with information about the bmp file types, occupy a total of 14 bytes. The second part is image data information, stored information related to the bitmap image, a total of 40 bytes, the third part is an optional palette, and finally the pixel information area.

Each portion specific meaning of the fields in the table below:

Accordance with the format above description, we define a few structures to describe them:

#pragma once

#include<stdio.h>
typedef unsigned int DWORD;  // 4bytes
typedef unsigned short WORD;  // 2bytes
typedef signed long LONG;  // 4bytes
typedef unsigned char BYTE;  // 1bytes


#pragma pack(push)
#pragma pack(1)// 修改默认对齐值
/*位图文件文件头结构体*/
typedef struct tagBITMAPFILEHEADER {
	WORD bFileType;
	DWORD bFileSize;
	WORD bReserved1;
	WORD bReserved2;
	DWORD bPixelDataOffset;
}BITMAPFILEHEADER; //14bytes
#pragma pack(pop)

/*位图文件信息头结构体*/
typedef struct tagBITMAPINFOHEADER {
	DWORD bHeaderSize;  // 图像信息头总大小(40bytes)
	LONG bImageWidth;  // 图像宽度(像素)
	LONG bImageHeight;  // 图像高度
	WORD bPlanes;  // 应该是0
	WORD bBitsPerPixel;  // 像素位数
	DWORD bCompression;  // 图像压缩方法
	DWORD bImageSize;  // 图像大小(字节)
	LONG bXpixelsPerMeter;  // 横向每米像素数
	LONG bYpixelsPerMeter;  // 纵向每米像素数
	DWORD bTotalColors;  // 使用的颜色总数,如果像素位数大于8,则该字段没有意义
	DWORD bImportantColors;  // 重要颜色数,一般没什么用
}BITMAPINFOHEADER; //40bytes

/*位图文件调色板结构体*/
typedef struct tagRGBQUAD {
	BYTE	rgbBlue;
	BYTE	rgbGreen;
	BYTE	rgbRed;
	BYTE	rgbReserved;
}RGBQUAD;

/*像素点RGB结构体*/
typedef struct tagRGB {
	BYTE blue;
	BYTE green;
	BYTE red;
}RGBDATA;

Pack #pragma (Push)
#pragma Pack (. 1)    
for canceling the automatic alignment of the structure, if not specified the size of the file header is not the structure may occupy 14 bytes, if the size of the mechanism on the right body can be output  sizeof (BITMAPFILEHEADER )  to get.

In fact these institutions bodies wingdi.h document also defined, if you use windows gdi structure can also be used directly defined.

After these well defined structures is to write a function to read the bitmap, the basic approach is to read from a file specified by fread size, specified number of elements, such as the following statement head for reading information:

fread(infoHead, sizeof(BITMAPINFOHEADER), 1, fp)

14 bytes from the read element of a stream file fp, infoHead memory area and place the pointer points, infoHead BITMAPINFOHEADER pointer type structure, the memory can also be directly defined in the corresponding variable dynamically allocated heap by malloc.

Paste the following source code used, forgive me for not writing much comment:

#define _CRT_SECURE_NO_DEPRECATE
#include"bmp.h"
#include<string.h>
#include<stdlib.h>
#include<stdio.h>


FILE* openBmpImage(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使用错误\n",mode);
		//文件打开失败,返回空指针
		return NULL;
	}
	if ((fp = fopen(fileName,mode)) == NULL) {
		fprintf(stderr, "打开文件:%s失败\n", fileName);
		return NULL;	
	}
	return fp;
}

void closeBmpImage(FILE* fp) {
	//关闭文件
	fclose(fp);
	//printf("已关闭文件\n");
	//释放文件指针
	free(fp);
	//printf("已释放文件指针\n");
}

BITMAPFILEHEADER* readBmpFileHead(FILE* fp) {
	//printf("%d\n", sizeof(BITMAPFILEHEADER));//这个大小是16Bytes没错
	BITMAPFILEHEADER* fileHead = (BITMAPFILEHEADER*)malloc(sizeof(BITMAPFILEHEADER));
	if (fileHead == NULL) {
		fprintf(stderr,"内存分配失败");
	}
	if (fread(fileHead, sizeof(BITMAPFILEHEADER), 1, fp) != 1) {
		fprintf(stderr,"文件头读取失败");
	}
	return fileHead;
}

BITMAPINFOHEADER* readBmpInfoHead(FILE* fp) {
	//printf("%d\n", sizeof(BITMAPINFOHEADER));
	BITMAPINFOHEADER* infoHead = (BITMAPINFOHEADER*)malloc(sizeof(BITMAPINFOHEADER));
	if (infoHead == NULL) {
		fprintf(stderr, "内存分配失败");
	}
	if (fread(infoHead, sizeof(BITMAPINFOHEADER), 1, fp) != 1) {
		fprintf(stderr, "信息头读取失败");
	}
	printf("信息头大小:%d字节\n", infoHead->bHeaderSize);
	printf("图片宽度:%d像素\n", infoHead->bImageWidth);
	printf("图片高度:%d像素\n", infoHead->bImageHeight);
	printf("颜色位数:%d位\n", infoHead->bBitsPerPixel);
	printf("横向每米像素数:%d个\n", infoHead->bXpixelsPerMeter);
	printf("纵向每米像素数:%d个\n", infoHead->bYpixelsPerMeter);
	printf("数据块大小:%d字节\n", infoHead->bImageSize);
	printf("位面数:%d\n", infoHead->bPlanes);
	printf("使用颜色总数:%d个\n", infoHead->bTotalColors);
	printf("重要颜色总数:%d个\n", infoHead->bImportantColors);
	printf("压缩算法:%d\n", infoHead->bCompression);
	
	return infoHead;
}

RGBDATA** readBmpDataToArr(FILE* fp) {
	int i = 0, j = 0;
	int width = 0, height = 0;
	BITMAPFILEHEADER* fileHead = readBmpFileHead(fp);
	BITMAPINFOHEADER* infoHead = readBmpInfoHead(fp);
	width = infoHead->bImageWidth;
	height = infoHead->bImageHeight;
	RGBDATA** data = createMatrix(width,height);
	//如果位数小于8则调色板有效
	if (infoHead->bBitsPerPixel < 8) {
		RGBQUAD* rgbQuad = (RGBQUAD*)malloc(sizeof(RGBQUAD));
		if(rgbQuad == NULL){
			printf("内存分配失败");
		}
		if (fread(rgbQuad, sizeof(rgbQuad), 1, fp) != 1) {
			printf("调色板读入失败");
		}
	}
	for (i = 0; i < height; i++) {
		for (j = 0; j < width; j++) {
			fread(&data[i][j], sizeof(RGBDATA), 1, fp);
		}
	}
	return data;
}

RGBDATA** createMatrix(int width,int height) {
	//动态创建二维数组
	RGBDATA** Matrix;
	int i;
	
	Matrix = (RGBDATA **)malloc(sizeof(RGBDATA*) * height);
	if (Matrix == NULL) {
		fprintf(stderr,"内存分配失败");
		return NULL;
	}
	
	for (i = 0; i < height; i++) {
		Matrix[i] = (RGBDATA *)malloc(sizeof(RGBDATA) * width);
		if (Matrix[i] == NULL) {
			fprintf(stderr, "内存分配失败");
			return NULL;
		}
	}
	return(Matrix);
}

没有太多内容值得细说,后面是测试代码:

#include<stdio.h>
#include"bmp.h"
int main() {
	//printf("%d",sizeof(unsigned short));
	//printf("%d",sizeof(unsigned int));
	//printf("%d",sizeof(unsigned long));
	//printf("%d",sizeof(unsigned char));
	FILE* fp = openBmpImage("lena.bmp","r");
	//BITMAPFILEHEADER* fileHead = readBmpFileHead(fp);
	//BITMAPINFOHEADER* infoHead = readBmpInfoHead(fp);
	RGBDATA ** data = readBmpDataToArr(fp);
	//谨慎,避免下标越界
	for (int i = 0; i < 512; i++) {
		for (int j = 0; j < 512; j++) {
			printf("第(%d,%d)像素:[%d,%d,%d] \n ", 511-i,j+1,data[i][j].blue, data[i][j].green, data[i][j].red);
		}
		printf("\n");
	}
	closeBmpImage(fp);
	getchar();
	return 0;
}

运行之后可以得到这样的结果:

注意每个像素得到的三个数值分别代表b,g,r分量,和习惯的rgb是反着的。

最后要注意,大多数位图的像素信息是自下而上的,也就是说代码中得到的数组的最后一行对应图片的第一行,以此类推。。

位图读取就是这样了,下一步再考虑写入bmp文件和像素数组的处理操作。。。

发布了7 篇原创文章 · 获赞 8 · 访问量 170

Guess you like

Origin blog.csdn.net/cxm2643199642/article/details/104043406