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

来源:互联网 发布:域名排行 编辑:程序博客网 时间:2024/06/15 14:54

这学期开了一门课,叫图像处理。也认识到了一个很牛逼的老师——小强老师。每节课都给我们讲一大堆道理,鼓励我们多动手写写代码,学点东西。于是在小强老师的刺激之下,我开始学习图像处理第一课,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_listargptr;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){BYTEstrFileName[50];// 存放要读取文件的文件名FILE*fp_bmp;// bmp文件的文件指针BITMAP* bmp;// bmp数据指针 DWORDvalve;//二值化阀值// 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———————————————————————








原创粉丝点击