这学期开了一门课,叫图像处理。也认识到了一个很牛逼的老师——小强老师。每节课都给我们讲一大堆道理,鼓励我们多动手写写代码,学点东西。于是在小强老师的刺激之下,我开始学习图像处理第一课,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———————————————————————