您的位置:首页 > 编程语言 > C语言/C++

BMP文件结构读写操作

2016-04-11 09:27 405 查看
之前一直都准备写blog来记录自己的学习,但是无奈拖延症!现在总算有时间有精力了,希望大家能共同学习,互相讨论,如果文中有什么不妥之处,还请您指出,谢谢。

今天写的是一篇有关位图操作的文章,主要是实现了bmp的读写操作,为什么要做这个呢? 因为这是基础啊,c语言做图像处理都能搞定,还有什么语言搞不定的呢!当然核心的还是算法,这些在后面会慢慢扩充。

说到BMP文件结构读写操作,我们就不得不先了解bmp文件的结构,什么这头啊,那头啊,巴拉巴拉一大堆,下面给两个参考网站,极力推荐:

1. 你所能用到的BMP格式介绍

2.bmp图像大小biSizeImage算法公式由来

下面我也简单介绍一下bmp文件的结构,加深加深印象啦!

BMP文件的结构

        BMP图像文件被分成4个部分:位图文件头(Bitmap File Header)、位图信息头(Bitmap Info Header)、颜色表(Color Map)和位图数据(即图像数据,Data Bits或Data Body)。

        第1部分为位图文件头BITMAPFILEHEADER,是一个结构体类型,该结构的长度是固定的,为14个字节。其定义如下:
typedef struct tagBITMAPFILEHEADER
{
WORD bfType;                   //位图文件类型,必须是0x424D,即字符串“BM”.
DWORD bfSize;              //位图文件大小,包括这14个字节。
WORD bfReserved1;         //Windows保留字,暂不用
WORD bfReserved2;
DWORD bfOffBits; 	    //从文件头到实际的位图数据的偏移字节数
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER,
*PBITMAPFILEHEADER;
        第2部分为位图信息头BITMAPINFOHEADER,也是一个结构体类型的数据结构,该结构的长度也是固定的,为40个字节(WORD为无符号16位整数,DWORD为无符号32位整数,LONG为32位整数)。其定义如下:
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;     	            //本结构的长度,为40个字节
LONG biWidth;    	            //位图的宽度,以像素为单位
LONG biHeight;    	            //位图的高度,以像素为单位
WORD biPlanes;                        //目标设备的级别,必须是1
WORD biBitCount                      //每个像素所占的位数(bit)
DWORD biCompression;           //位图压缩类型,一般都是未压缩的,取0
DWORD biSizeImage;               //实际的位图数据占用的字节数
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;                   //位图实际用到的颜色数,取0,颜色数为2的biBitCount次幂
DWORD biClrImportant;            //位图显示过程中重要的颜色数
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER,
*PBITMAPINFOHEADER;

        第3部分为颜色表。颜色表实际上是一个RGBQUAD结构的数组,数组的长度由biClrUsed指定(如果该值为零,则由biBitCount指定,即2的biBitCount次幂个元素)。RGBQUAD结构是一个结构体类型,占4个字节,其定义如下:

typedef struct tagRGBQUAD
{
BYTE rgbBlue;                 //该颜色的蓝色分量
BYTE rgbGreen;              //该颜色的绿色分量
BYTE rgbRed;                 //该颜色的红色分量
BYTE rgbReserved;        //保留
}RGBQUAD;
        有些位图需要颜色表;有些位图(如真彩色图)则不需要颜色表,颜色表的长度由BITMAPINFOHEADER结构中biBitCount分量决定。

        第4部分是位图数据,即图像数据,其紧跟在位图文件头、位图信息头和颜色表(如果有颜色表的话)之后,记录了图像的每一个像素值。对于有颜色表的位图,位图数据就是该像素颜色在调色板中的索引值;对于真彩色图,位图数据就是实际的R、G、B值(三个分量的存储顺序是B、G、R)。下面分别就2色、16色、256色和真彩色位图的位图数据进行说明:

        对于2色位图,用1位就可以表示该像素的颜色,所以1个字节能存储8个像素的颜色值。

 

        对于16色位图,用4位可以表示一个像素的颜色。所以一个字节可以存储2个像素的颜色值。

 

        对于256色位图,1个字节刚好存储1个像素的颜色值。

 

        对于真彩色位图,3个字节才能表示1个像素的颜色值。

        相信经过上面的洗礼,我们对bmp已经有了充分的了解,下面我们就可以开始读写bmp图像了!

对于以上的四个部分在Windows.h中都有定义,因此我们需要在程序中include这个头文件。

下面给出本次程序的源代码:

#include<stdio.h>
#include<Windows.h>   //包含位图文件结构定义

//函数声明
bool readBmp(char *bmpName);
bool saveBmp(char * bmpName, unsigned char *imgBuf,
int width, int height, int biBitCount,
RGBQUAD * pcolortable);

//定义需要的全局变量
unsigned char *pBmpBuf;   //指向bmp数据区的指针
int bmpWidth;			  //bmp图片的宽度
int bmpHeight;            //bmp图片的高度
RGBQUAD *pcolortable;     //指向颜色表的指针,注意:若是24位bmp图片则没有颜色表
int biBitCount;           //每一个像素需要的位数,若为8则一个像素需要8位来表示,即一个字节

int main()    //主函数入口
{
char readPath[] = "lena.bmp";         //bmp图片路径名,注意要把lena.bmp放在了工程目录下,否则就要加上绝对路径。
readBmp(readPath);

printf("  weight:%d\n",bmpWidth);
printf("  height:%d\n",bmpHeight);
printf("  bitCount:%d\n",biBitCount);

//char writePath[] = "lena0.bmp";
//saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pcolortable);

delete[]pBmpBuf;
if (biBitCount == 8)
delete[]pcolortable;
system("pause");

return 0;
}

bool readBmp(char *bmpName)  //读bmp图片
{
FILE * fp = fopen(bmpName, "rb");
if (fp == 0)
return 0;
fseek(fp, sizeof(BITMAPFILEHEADER), 0);           //跳过文件头,因为我们默认读取的就是bmp图片
BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);    //读取信息头
bmpWidth = head.biWidth;
bmpHeight = head.biHeight;
biBitCount = head.biBitCount;

int lineBytes = (bmpWidth*biBitCount / 8 + 3) / 4 * 4;    //为了保证每行的大小都是4的整数倍,可以直接带个数算算,多体会体会
if (biBitCount == 8)          //如果biBitCount等于8,则说明不是真彩色图像,需要颜色表
{
pcolortable = new RGBQUAD[256];
fread(pcolortable, sizeof(RGBQUAD), 256, fp);
}
pBmpBuf = new unsigned char[lineBytes*bmpHeight];
fread(pBmpBuf,lineBytes*bmpHeight,1, fp);
//	printf("Bmpsize:%d\n", lineBytes*bmpHeight);
fclose(fp);
return 1;
}
bool saveBmp(char * bmpName, unsigned char *imgBuf,    //存bmp图片
int width, int height, int biBitCount,
RGBQUAD * pcolortable)
{
if (!imgBuf) return 0;

int colortablesize = 0;
if (biBitCount == 8)
colortablesize = 1024;
int lineByte = (width*biBitCount / 8 + 3) / 4 * 4;
FILE * fp = fopen(bmpName,"wb");
if (fp == 0) return 0;
BITMAPFILEHEA
4000
DER fileHead;

fileHead.bfType = 0x4D42; //bmp类型
fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ colortablesize + height*lineByte;
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
fileHead.bfOffBits = 54 + colortablesize;
fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);

BITMAPINFOHEADER Head;
Head.biBitCount = biBitCount;
Head.biClrImportant = 0;
Head.biClrUsed = 0;
Head.biCompression = BI_RGB;
Head.biHeight = height;
Head.biPlanes = 1;
Head.biSize = 40;
Head.biSizeImage = height*lineByte ;
Head.biWidth = width;
Head.biXPelsPerMeter = 0x270E;
Head.biYPelsPerMeter = 0x270E;

fwrite(&Head, sizeof(BITMAPINFOHEADER), 1, fp);

if (biBitCount == 8)
fwrite(pcolortable, sizeof(RGBQUAD), 256, fp);
fwrite(imgBuf, height*lineByte+2, 1, fp);

printf("datasize:%d\n", height*lineByte);
fclose(fp);

return 1;

}


下图是在VS2015中运行的结果:



我们打开该图片的信息,可以右键图片,在属性中查看详细信息:



bingo!

大伙儿有任何问题,都可以直接评论,谢谢!

用MFC实现bmp图像的显示与处理,请看下一篇文章!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bmp c语言 图像处理 MFC