读BMP文件——图像处理第一课

这学期开了一门课,叫图像处理。也认识到了一个很牛逼的老师——小强老师。每节课都给我们讲一大堆道理,鼓励我们多动手写写代码,学点东西。于是在小强老师的刺激之下,我开始学习图像处理第一课,BMP文件的读取。废话不多说,上代码。

—————————————————————————分割线—————————————————————————

第一部分:BMP文件格式。

文件名:bmp_head.h

#ifndef _BMPHEAD_H_
#define _BMPHEAD_H_

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char   BYTE;   		//  字符型
typedef unsigned short  WORD;   		//  短整型
typedef unsigned long   DWORD;  		//  长整形
typedef unsigned char* 	PCSIZEIMAGE; 	//	指向图片数据的指针           
typedef long 			LONG;

#pragma pack(push)	//	保存对齐状况
#pragma pack(2)		//	设置为2字节对齐 

// 位图文件头信息结构定义(BMP文件头14字节)
// BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息
// 其中不包含文件类型信息(由于结构体的内存结构决定,要是加了的话将不能正确读取文件信息)
typedef struct tagBITMAPFILEHEADER {
    WORD  bfType;       // 位图文件的类型,必须为BM(4d42)(1-2字节)
    DWORD bfSize;       // 位图文件的大小,以字节为单位(3-6字节)
    WORD  bfReserved1;  // 位图文件保留字,必须为0(7-8字节)
    WORD  bfReserved2;  // 位图文件保留字,必须为0(9-10字节)
    DWORD bfOffBits;    // 实际位图数据的偏移字节数,即前三个部分长度之和
                        // 文件头的偏移量表示(11-14字节)
} BITMAPFILEHEADER;


// 位图信息头(40字节)
// BMP位图信息头数据用于说明位图的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{
    DWORD biSize;         // 本结构所占用字节数(40字节)(15-18字节)
    LONG  biWidth;        // 位图的宽度,以像素为单位    (19-22字节)
    LONG  biHeight;       // 位图的高度,以像素为单位    (23-26字节)
    WORD  biPlanes;       // 目标设备的级别(平面数),必须为1    (27-28字节)
    WORD  biBitCount;     // 每个像素所需的位数,必须是1(双色),4(16色),8(256色),
                          // 24(真彩色)或32(增强型真彩色)之一     (29-30字节)
    DWORD biCompression;  // 位图压缩,必须是0(不压缩),1(BI_RLE8压缩类型),
                          // 或2(BI_RLE4压缩类型)之一             (31-34字节)
    DWORD biSizeImage;    // 位图的大小,以字节为单位             (35-38字节)
    LONG  biXPelsPerMeter;// X方向(水平方向)分辨率,每米像素数  (39-42字节)
    LONG  biYPelsPerMeter;// Y方向(垂直方向)分辨率,每米像素数  (43-46字节)
    DWORD biClrUsed;      // 使用的颜色数,如果为0,则表示默认值(2^颜色位数) (47-50字节)
    DWORD biClrImportant; // 重要颜色数,如果为0,则表示所有颜色都是重要的   (51-54字节)
} BITMAPINFOHEADER;


// 调色板Palette(24位和32位是不需要调色板的)。
//(似乎是调色板结构体个数等于使用的颜色数。)
typedef struct tagRGBQUAD {
    BYTE rgbBlue;     // 该颜色的蓝色分量(值范围为0-255)
    BYTE rgbGreen;    // 该颜色的绿色分量(值范围为0-255)
    BYTE rgbRed;      // 该颜色的红色分量(值范围为0-255)
    BYTE rgbReserved; // 保留值,必须为0
} RGBQUAD;

// BMP文件结构体。
// 用一个结构体可获得整个BMP文件的指针。
typedef struct tagBITMAP {
	BITMAPFILEHEADER*	pbitMapFileHeader;
	BITMAPINFOHEADER*	pbitMapInfoHeader;
	RGBQUAD*			prgbQuad;
	PCSIZEIMAGE*		pcSizeImage;
} BITMAP;

extern BITMAP* Read_Bmp_Malloc(FILE *fp_bmp);
extern void Delete_Bmp_Free(BITMAP* bmp);

extern void Com_Error(const BYTE* format,...);

#pragma pack(pop)	//	恢复对齐状况

#endif

文件名:bmp_head.c

#include <stdio.h>
#include <stdarg.h>
#include "bmp_head.h"

/*
 *	函数名:		Read_Bmp_Malloc
 *	传入参数:	BITMAP文件指针 
 *	传出参数:	无
 *	返回值:	指向BITMAP结构体的指针
 *	注意:		每次调用Read_Bmp_Malloc函数应对应调用Delete_Bmp_Free函数,
 *				否则会存在内存泄露! 
*/
BITMAP* Read_Bmp_Malloc(FILE *fp_bmp)
{
	BITMAP* bmp;			//	指向BMP的指针.
	LONG 	data_size_row;	//	BMP图像一行有多少bit.
	
	if (NULL==(bmp=(BITMAP*)malloc(sizeof(BITMAP))))
	{
		Com_Error("Heap overflow !:%s\n","bmp");
		exit(1);
	}
	
	// read the bitmapfileheader.
	// if the type is not the "BM",that the file is not the bmp file.	
	if (NULL==(bmp->pbitMapFileHeader = (BITMAPFILEHEADER*)malloc(14)))
	{
		Com_Error("Heap overflow !:%s\n","bmp->pbitMapFileHeader");
		goto error_1;	
	}
	fread(bmp->pbitMapFileHeader,sizeof(BITMAPFILEHEADER),1,fp_bmp);
	if (0x4d42!=(*bmp->pbitMapFileHeader).bfType)
	{
		Com_Error("File error:the file is not the bmp file!\n");		
		goto error_2;
	}
	
	// Read the bitmapinfoheader.	
	if (NULL==(bmp->pbitMapInfoHeader = (BITMAPINFOHEADER*)malloc(40)))
	{
		Com_Error("Heap overflow !:%s\n","bmp->pbitMapInfoHeader");
		goto error_2;	
	}
	fread(bmp->pbitMapInfoHeader,sizeof(BITMAPINFOHEADER),1,fp_bmp);

	// Read the RGBQUAD.
	if (16>(*bmp->pbitMapInfoHeader).biBitCount)
	{
		if (NULL==(bmp->prgbQuad = (RGBQUAD*)malloc(4* \
			(1<<(*bmp->pbitMapInfoHeader).biBitCount))))
		{
			Com_Error("Heap overflow !:%s\n","bmp->prgbQuad");
			goto error_3;	
		}
		fread(bmp->prgbQuad,4,(1<<(*bmp->pbitMapInfoHeader).biBitCount),fp_bmp);		
	}
	else
	{
		if (16==(*bmp->pbitMapInfoHeader).biBitCount)
		{
			Com_Error("BitCount:The Bitcount is 16 BMP maps!\n"
				"I can not handle this type.\n");
			goto error_3;
		}
		bmp->prgbQuad = NULL;
		Com_Error("BitCount:The BMP maps is no Palette!\n");
	}
	
	// Move to the head of bitmap data.
	fseek(fp_bmp,(*bmp->pbitMapFileHeader).bfOffBits,SEEK_SET);	
	
	// Malloc the space and read the image data. 
	data_size_row = (DWORD)((((*bmp->pbitMapInfoHeader).biWidth) \
		* (*bmp->pbitMapInfoHeader).biBitCount +31)/32)*4;	
		
	if (NULL==(bmp->pcSizeImage = (PCSIZEIMAGE*)malloc(data_size_row * \
		(*bmp->pbitMapInfoHeader).biHeight)))
	{
		Com_Error("Heap overflow !:%s\n","bmp->pcSizeImage");
		goto error_4;		
	}
	
	fread(bmp->pcSizeImage,data_size_row,
		(*bmp->pbitMapInfoHeader).biHeight,fp_bmp);
		
	//	Return the pointer which poin to the struct bmp.
	return bmp;
	
error_4:
	free(bmp->prgbQuad);
	bmp->prgbQuad = NULL; 
error_3:
	free(bmp->pbitMapInfoHeader);
	bmp->pbitMapInfoHeader = NULL;
error_2:
	free(bmp->pbitMapFileHeader); 
	bmp->pbitMapFileHeader = NULL;
error_1:
	free(bmp);
	bmp = NULL;
	exit(1); 
}

/*
 *	函数名:		Delete_Bmp_Free
 *	传入参数:	BITMAP结构体指针 
 *	传出参数:	无
 *	返回值:	无
 *	说明:		本函数用于释放不再使用的BITMAP结构体占用的内存。 
*/
void Delete_Bmp_Free(BITMAP* bmp)
{
	free(bmp->pcSizeImage);
	bmp->pcSizeImage = NULL;
	if (16>(*bmp->pbitMapInfoHeader).biBitCount)
	{
		free(bmp->prgbQuad);
		bmp->prgbQuad = NULL; 
	}
	free(bmp->pbitMapInfoHeader);
	bmp->pbitMapInfoHeader = NULL;
	free(bmp->pbitMapFileHeader);
	bmp->pbitMapFileHeader = NULL;
	free(bmp);
	bmp = NULL;
}



/*
 *	函数名:		Com_Error
 *	传入参数:	错误提示描述及可选描述数
 *	传出参数:	无
 *	返回值:	无
*/
void Com_Error(const BYTE* format,...)
{
	char 	com_errorMessage[4096];
	va_list	argptr;
	va_start (argptr,format);
	_vsnprintf(com_errorMessage,4096,format,argptr);
	va_end (argptr);
	printf (com_errorMessage);
}
———————————————————————————分割线———————————————————————

以上是BMP文件格式的部分,附带从一个文件中读取整个BMP数据与从堆中把读取的BMP数据删除的两个函数,最后一个函数是用来显示错误输出的,你可以把这个函数看成是给printf函数取了一个叫Com_error的别名,专门用来进行错误提醒的。

为了能测试我们上面的两个文件,我们需要用一个main函数来调用我们上面的程序,进行一些简单的测试。

———————————————————————————分割线———————————————————————

第二部分:主函数

文件名:bmp.c

#include <stdio.h>
#include <stdlib.h>
#include "bmp_head.h"

int main(int argv, char **argc)
{
	BYTE	strFileName[50];		// 	存放要读取文件的文件名
	FILE*	fp_bmp;				// 	bmp文件的文件指针
	BITMAP* bmp;				// 	bmp数据指针 
	DWORD	valve;				//	二值化阀值

	// Open the file.
	printf("**本程序用C语言编程来读取BMP文件某一像素点的数据**\n"
		"请输入一个bmp文件:");
	scanf("%s",strFileName);
	fflush(stdin);
	if (NULL == (fp_bmp=(fopen(strFileName,"rb"))))
	{
		Com_Error("Open bmp:open the file failed!:%s \n",strFileName);
		exit(1);
	}
	printf("成功读取!\n\n");
		
	bmp = Read_Bmp_Malloc(fp_bmp);
	
	printf("BITMAPFILEHEADER\n");
	printf("bfType=%x,bfSize=%x,bfReserved1=%x,bfReserved2=%x,bfOffBits=%x\n",
		(*bmp->pbitMapFileHeader).bfType,(*bmp->pbitMapFileHeader).bfSize,
		(*bmp->pbitMapFileHeader).bfReserved1,(*bmp->pbitMapFileHeader).bfReserved2,
		(*bmp->pbitMapFileHeader).bfOffBits,);
	
		printf("BITMAPINFOHEADER");
	printf("biSize=%x,biWidth=%x,biHeight=%x,biPlanes=%x,biBitCount=%x,"
			"biCompression=%x,biSizeImage=%x,biXPelsPerMeter=%x,biYPelsPerMeter=%x,"
			"biClrUsed=%x,biClrImportant=%x",(*bmp->pbitMapInfoHeader).biSize,
			(*bmp->pbitMapInfoHeader).biWidth,(*bmp->pbitMapInfoHeader).biHeight,
			(*bmp->pbitMapInfoHeader).biPlanes,(*bmp->pbitMapInfoHeader).biBitCount,
			(*bmp->pbitMapInfoHeader).biCompression,(*bmp->pbitMapInfoHeader).biSizeImage,
			(*bmp->pbitMapInfoHeader).biXPelsPerMeter,(*bmp->pbitMapInfoHeader).biYPelsPerMeter,
			(*bmp->pbitMapInfoHeader).biClrUsed,(*bmp->pbitMapInfoHeader).biClrImportant);

	Delete_Bmp_Free(bmp);

	fclose(fp_bmp);

	return 0;
	
error_1:
	Delete_Bmp_Free(bmp);
	exit(1);
}
———————————————————————————分割线———————————————————————

上面的main函数只是打印出数据头的两部分,还有两部分作者偷了个懒,没有写出来。但是在后面的学习中,我会用到这些数据,经过检测,数据已经完全被读出来了。


好了,今天第一课的学习到这里就告一段落了。后面有时间的话,我应该还会继续写下去。

———————————————————————————END———————————————————————








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值