简介
BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。BMP位图文件默认的文件扩展名是BMP或者bmp。
BMP图片格式
BMP文件总体上由4部分组成,分别是位图文件头、位图信息头、调色板和图像数据
位图文件头(bitmap-file header)
BMP文件头数据结构共14个字节,包含以下信息:
typedef struct{
unsigned short bfType; //文件类型,具体值为0x4D42
int bfSize; //文件大小,以字节为单位
unsigned short bfReserved1; //保留值,必须为0
unsigned short bfReserved2; //保留值,必须为0
int bfOffBits; //从文件开头到具体图像数据的字节偏移量(具体文件头(14byte)+位图信息头(40byte)+调色板)
}BITMAPFILEHEADER;
位图信息头(bitmap-information header)
图片信息头(40字节)存储着图像的尺寸,颜色索引,位平面数等信息,其结构体包括:
typedef struct{
unsigned int biSize; //位图信息头的大小,均为40字节
int biWidth; //位图宽度,单位为像素
int biHeight; //位图高度,单位为像素
unsigned short biPlanes; //位图的平面数,设置为1
unsigned short biBitCount; //像素位数,有1,4,8,16,24,32等
unsigned int biCompression; //位图压缩类型,0为不压缩、1为BI_RLE8、2为BI_RLE4(30-33)
unsigned int biSizeImage; //图像数据部分大小,等于bfSize-bfOffBits (34-37)
int biXPelsPerMeter; // 用像素/米表示的水平分辨率
int biYPelsPerMeter; // 用像素/米表示的垂直分辨率
unsigned int biClrUsed; // 调色板中的颜色索引数,0为图片有调色板
unsigned int biClrImportant; // 重要颜色索引的数目,0表示都重要(50-53)
}BITMAPINFOHEADER;
彩色表/调色板(color table)
调色板,由颜色索引数决定【可以没有该项数据结构】
typedef struct
{
unsigned char rgbBlue; //蓝色分量亮度
unsigned char rgbGreen; //绿色分量亮度
unsigned char rgbRed; //红色分量亮度
unsigned char rgbReserved;
}RGBQUAD;
位图数据(bitmap-data)
位图数据记录了位图的每一个像素值。
像素是从下到上、从左到右保存的。
每个像素使用一个或者多个字节表示。
如果一个图像水平线的字节数不是4的倍数,这行就使用空字节补齐,通常是ASCII码0。
如果图片是24位的彩色图,则RGB的顺序为B0G0R0B1G1R1…。
代码编写
本代码实现彩色图转灰度图的功能。
#include<stdio.h>
#pragma pack(1)
typedef struct{
unsigned short bfType; //文件类型,具体值为0x4D42
int bfSize; //文件大小,以字节为单位
unsigned short bfReserved1; //保留值,必须为0
unsigned short bfReserved2; //保留值,必须为0
int bfOffBits; //从文件开头到具体图像数据的字节偏移量(具体文件头(14byte)+位图信息头(40byte)+调色板)
}BITMAPFILEHEADER;
typedef struct{
unsigned int biSize; //位图信息头的大小,均为40字节
int biWidth; //位图宽度,单位为像素
int biHeight; //位图高度,单位为像素
unsigned short biPlanes; //位图的平面数,设置为1
unsigned short biBitCount; //像素位数,有1,4,8,16,24,32等
unsigned int biCompression; //位图压缩类型,0为不压缩、1为BI_RLE8、2为BI_RLE4(30-33)
unsigned int biSizeImage; //图像数据部分大小,等于bfSize-bfOffBits (34-37)
int biXPelsPerMeter; // 用像素/米表示的水平分辨率
int biYPelsPerMeter; // 用像素/米表示的垂直分辨率
unsigned int biClrUsed; // 调色板中的颜色索引数,0为图片有调色板
unsigned int biClrImportant; // 重要颜色索引的数目,0表示都重要(50-53)
}BITMAPINFOHEADER;
typedef struct
{
unsigned char rgbBlue; //蓝色分量亮度
unsigned char rgbGreen; //绿色分量亮度
unsigned char rgbRed; //红色分量亮度
unsigned char rgbReserved;
}RGBQUAD;
void showbmp(BITMAPFILEHEADER bmp){
printf("****************************************文件头信息打印*******************************************************\n");
printf("文件类型:%x\n",bmp.bfType);
printf("文件大小=%d\n",bmp.bfSize);
printf("文件保留值1=%d\n",bmp.bfReserved1);
printf("文件保留值2=%d\n",bmp.bfReserved2);
printf("文件信息区的大小=%d\n",bmp.bfOffBits);
}
void showbmpinfo(BITMAPINFOHEADER info){
printf("结构体大小=%d\n",info.biSize);
printf("图像宽=%d\n",info.biWidth);
printf("图像高=%d\n",info.biHeight);
printf("图像位面数=%d\n",info.biPlanes);
printf("像素位数=%d\n",info.biBitCount);
printf("位图压缩类型:%d\n",info.biCompression);
printf("图像数据部分大小:%d\n",info.biSizeImage);
printf("水平分辨率:%d\n",info.biXPelsPerMeter);
printf("垂直分辨率:%d\n",info.biYPelsPerMeter);
printf("位图颜色表中的颜色数:%d\n",info.biClrUsed);
printf("重要颜色索引的数目:%d\n",info.biClrImportant);
}
int main(){
BITMAPFILEHEADER bmp;
BITMAPINFOHEADER bmp_info;
//
FILE* fp1;
FILE* fp2;
fp1=fopen("C:\\Users\\qjm\\Desktop\\test.bmp","rb");
fread(&bmp,14,1,fp1);
fread(&bmp_info,40,1,fp1);
//show infomation
showbmp(bmp);
showbmpinfo(bmp_info);
//
int lineByte=(bmp_info.biWidth*3+3)/4*4; //4字节对齐后一行的字节数,单位字节
int lineByte_gray=(bmp_info.biWidth+3)/4*4;
printf("lineByte=%d\n",lineByte);
printf("lineByte_gray=%d\n",lineByte_gray);
//
unsigned char* src_img=new unsigned char[bmp_info.biSizeImage];
unsigned char* dest_img=new unsigned char[bmp_info.biSizeImage/3];
fread(src_img,1,bmp_info.biSizeImage,fp1);
for(int i=0;i<bmp_info.biHeight;i++)
for(int j=0;j<bmp_info.biWidth;j++){
unsigned char b=src_img[i*lineByte+j*3+0];
unsigned char g=src_img[i*lineByte+j*3+1];
unsigned char r=src_img[i*lineByte+j*3+2];
unsigned char gray=((unsigned int)r+(unsigned int)g+(unsigned int)b)/3;
dest_img[i*lineByte_gray+j]=gray;
}
fclose(fp1);
//
printf("****************************************************************\n");
RGBQUAD* pRgbQuards=new RGBQUAD[256];
for(int i=0;i<256;i++)
{
pRgbQuards[i].rgbBlue=i;
pRgbQuards[i].rgbRed=i;
pRgbQuards[i].rgbGreen=i;
}
fp2=fopen("C:\\Users\\qjm\\Desktop\\test_out.bmp","wb");
bmp_info.biBitCount=24/3;
bmp_info.biSizeImage=bmp_info.biSizeImage/3;
bmp.bfOffBits=14+40+256*sizeof(RGBQUAD); //增加了颜色表
bmp.bfSize=bmp.bfOffBits+bmp_info.biSizeImage;
//
showbmp(bmp);
showbmpinfo(bmp_info);
//
fwrite(&bmp,1,14,fp2);
fwrite(&bmp_info,1,40,fp2);
fwrite(pRgbQuards,1,256*sizeof(RGBQUAD),fp2);
fwrite(dest_img,1,bmp_info.biSizeImage,fp2);
fclose(fp2);
return 0;
}
待测试图片:
转灰度图后的效果: