bmp文件格式详细解析

转载 2015年07月09日 15:36:18
先区分几个概念:16色和16位色一样吗?
不一样!
颜色位数,即是用多少位字节表示的值,每一位可以表示0和1两值。通常图片的颜色深度,简称色深,就是用位数来表示的,所以,我通常会看到8位色,16位色,24位色和32位色。
而我们在其它地方看到的又是16色,256色,16777216色等等,这些怎么一回事呢? 
16色即代表16种颜色,256色即256种颜色,8位色就是用8个位来表示的颜色,即2的8次方,就是256色,16位色2的16次方,就是65536色,24位即16777216色,32位即4294967296色 
他们之间并不存在转换关系,只有兼容关系,你不可能将红色转换为蓝色,也不可能将一个黑白的图片转换成一个彩色图片,但可以将彩色图片变成黑白图片,就像以前的黑白电视机播放彩色录像始终是黑白的,而彩色电视机播放黑白的录像还是黑白的,当由多到少时,它会丢失颜色数据,当由少到多时,并不会还原丢失的数据。
好了,切入正题:
bmp文件结构解析:
一个bmp图片最多由4大部分组成:BITMAPFILEHEADER结构体,BITMAPINFOHEADER结构体,RGBQUAD结构体(这个结构体可以有,也可以没有),DIB数据区。其中DIB意思就是Device-Independent Bitmap(设备无关位图)。

一个bmp文件以BITMAPFILEHEADER结构体开始,

typedef struct tagBITMAPFILEHEADER {

WORD bfType;//固定为0x4d42;
    DWORD bfSize; //文件大小
    WORD bfReserved1; //保留字,不考虑
    WORD bfReserved2; //保留字,同上
    DWORD bfOffBits; //实际位图数据的偏移字节数,即前三个部分长度之和
    } BITMAPFILEHEADER;

 BITMAPFILEHEADER的第1个属性是bfType(2字节),这里恒定等于0x4D42。由于内存中的数据排列高位在左,低位在右,所以内存中从左往右看就显示成(42 4D),所以在winhex中头两个 字节显示为(42 4D)就是这样形成的,以后的数据都是这个特点,不再作重复说明。

BITMAPFILEHEADER的第2个属性是bfSize(4字节),表示整个bmp文件的大小。

BITMAPFILEHEADER的第3个、第4个属性分别是bfReserved1bfReserved2(2字节),这里是2个保留属性,都为0,这里等于&H00000x0000

BITMAPFILEHEADER的第5个属性是bfOffBits(4字节),表示DIB数据区在bmp文件中的位置偏移量,比如等于0x00000076=118,表示数据区从文件开始往后数的118字节开始。

 BITMAPFILEHEADER结构体这里就讲完了,大家会发现BITMAPFILEHEADER只占了bmp文件开始的14字节长度,但需要 特别说明的是:我们在编程时,经常是以二进制的形式打开一个bmp文件,然后将其开头的14个字节读入自己定义的BITMAPFILEHEADER结构体中,如果按上述定义结构体的方式,需要注意,这个自己定义的结构体在内存中由于字节对齐,会占用16字节的空间,因而直接读入16字节,按字节顺序赋值给结构体,出来的数据是错误的,这样的话,可以先将这14字节的数据读入到一个缓冲器中,然后从缓冲器中按字节对结构体进行赋值。详细程序见后附录。鉴于此中原因,vb中定义一个BITMAPFILEHEADER结构体变量,其长度占了16个字节,原因就是第1个属性本来应该只分配2个字节,但实际被 分配了4个字节,多出来2个字节,所以如果想保存一张bmp图片,写入BITMAPFILEHEADER结构体时一定要注意这一点。

接下来是BITMAPINFO结构体部分。BITMAPINFO段由两部分组成:BITMAPINFOHEADER结构体和RGBQUAD结构 体。其中RGBQUAD结构体表示图片的颜色信息,有些时候可以省略,一般的24位图片和32位图片都不带RGBQUAD结构体,因为DIB数据区直接表 示的RGB值,一般4位图片和8位图片才带有RGBQUAD结构体。(多少位的图片就是用多少位来表示一个颜色信息,例如4位图片表示用4bit来表示 一个颜色信息。)一个bmp文件中有没有RGBQUAD结构体,可以根据前面BITMAPFILEHEADER结构体的第5个属性bfOffBits来判 断,因为BITMAPINFOHEADER结构体长度为40bit,如果BITMAPINFOHEADER结构体结束后还未到DIB数据区的偏移量,就说 明接下来的数据是RGBQUAD结构体部分。

下面进入正题BITMAPINFOHEADER部分。

typedef struct tagBITMAPINFOHEADER{
 //public:
 DWORD biSize; //指定此结构体的长度,为40
 LONG biWidth; //位图宽
 LONG biHeight; //位图高
 WORD biPlanes; //平面数,为1
 WORD biBitCount; //采用颜色位数,可以是1,2,4,8,16,24,新的可以是32
 DWORD biCompression; //压缩方式,可以是0,1,2,其中0表示不压缩
 DWORD biSizeImage; //实际位图数据占用的字节数
 LONG biXPelsPerMeter; //X方向分辨率
 LONG biYPelsPerMeter; //Y方向分辨率
 DWORD biClrUsed; //使用的颜色数,如果为0,则表示默认值(2^颜色位数)
 DWORD biClrImportant; //重要颜色数,如果为0,则表示所有颜色都是重要的
} BITMAPINFOHEADER;

BITMAPINFOHEADER的第1个属性是biSize(4字节),表示BITMAPINFOHEADER结构体的长度,最常见的长度是40字节。

BITMAPINFOHEADER的第2个属性是biWidth(4字节),表示bmp图片的宽度

BITMAPINFOHEADER的第3个属性是biHeight(4字节),表示bmp图片的高度

BITMAPINFOHEADER的第4个属性是biPlanes(2字节),表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1,这里等于0x0001

BITMAPINFOHEADER的第5个属性是biBitCount(2字节),表示bmp图片的颜色位数,即1位图(单色或二值图像),8位图,16位图,24位图、32位图等等。

BITMAPINFOHEADER的第6个属性是biCompression(4字节),表示图片的压缩属性,bmp图片是不压缩的,等于0,所以这里为0x00000000

BITMAPINFOHEADER的第7个属性是biSizeImage(4字节),表示bmp图片数据区的大小,当上一个数值biCompression等于0时,这里的值可以省略不填,所以这里等于0x00000000

BITMAPINFOHEADER的第8个属性是biXPelsPerMeter(4字节),表示图片X轴每米多少像素,可省略,这里等于0x00000EC3=3779像素/米。

BITMAPINFOHEADER的第9个属性是biYPelsPerMeter(4字节),表示图片Y轴每米多少像素,可省略,这里等于0x00000EC3=3779像素/米。

BITMAPINFOHEADER的第10个属性是biClrUsed(4字节),表示使用了多少个颜色索引表,一般biBitCount属性小于16才会用到,等于0时表示有2^biBitCount个颜色索引表,所以这里仍等于0x00000000

BITMAPINFOHEADER的第11个属性是biClrImportant(4字节),表示有多少个重要的颜色,等于0时表示所有颜色都很重要,所以这里等于0x00000000

至此BITMAPINFOHEADER结构体结束。

由于这个图片到这里还未到达DIB数据区的偏移量,或者说由于BITMAPINFOHEADER的第5个属性是biBitCount<16,也就是在1位图(只有两种颜色),4位图(只有2^4=16种颜色),8位图(只有2^8=256种颜色)的情况下,此时会有颜色表,也就是接下来的部分:RGBQUAD结构体。

//调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。24位和32位是不需要调色板的。
//(调色板结构体个数等于使用的颜色数,即是多少色图就有多少个,4位图16色,就有16个RGBQUAD结构体。)

typedef 
struct tagRGBQUAD {
//public:
BYTE rgbBlue; //该颜色的蓝色分量
BYTE rgbGreen; //该颜色的绿色分量
BYTE rgbRed; //该颜色的红色分量
BYTE rgbReserved; //保留值
} RGBQUAD;

这里举个4位图也就是16色图的例子:一 RGBQUAD结构体只占用4字节空间,从左到右每个字节依次表示(蓝色,绿色,红色,未使用)。举例的这个图片我数了数总共有16RGBQUAD 构体,由于该图片是4位图,2^4正好等于16,所以它把16种颜色全部都枚举出来了,这些颜色就是一个颜色索引表。颜色索引表编号从0开始,总共16 颜色,所以编号为0-15。从winhex中可以看到按照顺序,这16RGBQUAD结构体依次为:

编号:(蓝,绿,红,空)

0号:(00000000)

1号:(00008000)

2号:(00800000)

3号:(00808000)

4号:(80000000)

5号:(80008000)

6号:(80800000)

7号:(80808000)

8号:(C0C0C000)

9号:(0000FF00)

10号:(00FF0000)

11号:(00FFFF00)

12号:(FF000000)

13号:(FF00FF00)

14号:(FFFF0000)

15号:(FFFFFF00)

到这里,正好满足DIB数据区的偏移量,所以后面的字节就是图片内容了。这里需要提醒的是所有的DIB数据扫描行是上下颠倒的,也就是说一幅图片先绘制底部的像素,再绘制顶部的像素,所以这些DIB数据所表示的像素点就是从图片的左下角开始,一直表示到图片的右上角。

 

程序附录:

// std.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


//ReadBitMap
//
#include 
#include 
#include 
#include 
#include


#define WIDTHBYTES(bits) (((bits)+31)/32*4)

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef long LONG;


//位图文件头信息结构定义
//其中不包含文件类型信息(由于结构体的内存结构决定,要是加了的话将不能正确读取文件信息)

typedef struct tagBITMAPFILEHEADER {

 WORD bfType;//固定为0x4d42
 DWORD bfSize; //文件大小
 WORD bfReserved1; //保留字,不考虑
 WORD bfReserved2; //保留字,同上
 DWORD bfOffBits; //实际位图数据的偏移字节数,即前三个部分长度之和
} BITMAPFILEHEADER;


//信息头BITMAPINFOHEADER,也是一个结构,其定义如下:

typedef struct tagBITMAPINFOHEADER{
 //public:
 DWORD biSize; //指定此结构体的长度,为40
 LONG biWidth; //位图宽
 LONG biHeight; //位图高
 WORD biPlanes; //平面数,为1
 WORD biBitCount; //采用颜色位数,可以是1,2,4,8,16,24,新的可以是32
 DWORD biCompression; //压缩方式,可以是0,1,2,其中0表示不压缩
 DWORD biSizeImage; //实际位图数据占用的字节数
 LONG biXPelsPerMeter; //X方向分辨率
 LONG biYPelsPerMeter; //Y方向分辨率
 DWORD biClrUsed; //使用的颜色数,如果为0,则表示默认值(2^颜色位数)
 DWORD biClrImportant; //重要颜色数,如果为0,则表示所有颜色都是重要的
} BITMAPINFOHEADER;


//调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。24位和32位是不需要调色板的。
//(似乎是调色板结构体个数等于使用的颜色数。)

typedef struct tagRGBQUAD {
 //public:
 BYTE rgbBlue; //该颜色的蓝色分量
 BYTE rgbGreen; //该颜色的绿色分量
 BYTE rgbRed; //该颜色的红色分量
 BYTE rgbReserved; //保留值
} RGBQUAD;

 

void showBmpHead(BITMAPFILEHEADER* pBmpHead)
{
 printf("位图文件头:\n");
 printf("bmp格式标志bftype:0x%x\n",pBmpHead->bfType );
 printf("文件大小:%d\n",pBmpHead->bfSize);
 printf("保留字:%d\n",pBmpHead->bfReserved1);
 printf("保留字:%d\n",pBmpHead->bfReserved2);
 printf("实际位图数据的偏移字节数:%d\n",pBmpHead->bfOffBits);

}


void showBmpInforHead(tagBITMAPINFOHEADER* pBmpInforHead)
{
 printf("位图信息头:\n");
 printf("结构体的长度:%d\n",pBmpInforHead->biSize);
 printf("位图宽:%d\n",pBmpInforHead->biWidth);
 printf("位图高:%d\n",pBmpInforHead->biHeight);
 printf("biPlanes平面数:%d\n",pBmpInforHead->biPlanes);
 printf("biBitCount采用颜色位数:%d\n",pBmpInforHead->biBitCount);
 printf("压缩方式:%d\n",pBmpInforHead->biCompression);
 printf("biSizeImage实际位图数据占用的字节数:%d\n",pBmpInforHead->biSizeImage);
 printf("X方向分辨率:%d\n",pBmpInforHead->biXPelsPerMeter);
 printf("Y方向分辨率:%d\n",pBmpInforHead->biYPelsPerMeter);
 printf("使用的颜色数:%d\n",pBmpInforHead->biClrUsed);
 printf("重要颜色数:%d\n",pBmpInforHead->biClrImportant);
}

void showRgbQuan(tagRGBQUAD* pRGB)
{
 printf("(%-3d,%-3d,%-3d) ",pRGB->rgbRed,pRGB->rgbGreen,pRGB->rgbBlue);

}

 

void main()
{

 BITMAPFILEHEADER bitHead;
 BITMAPINFOHEADER bitInfoHead;
 FILE* pfile;

 char strFile[50];
 char *BmpFileHeader;
 WORD *temp_WORD;
 DWORD *temp_DWORD;
 printf("please input the .bmp file name:\n");
 scanf("%s",strFile);
 
 pfile = fopen(strFile,"rb");//打开文件
    BmpFileHeader=(char *)calloc(14,sizeof(char));
 if(pfile!=NULL)
 {
  printf("file bkwood.bmp open success.\n");
  //读取位图文件头信息
  
  
  
  fread(BmpFileHeader,1,14,pfile);
  temp_WORD=(WORD* )(BmpFileHeader);
  bitHead.bfType=*temp_WORD;
  if(bitHead.bfType != 0x4d42)
  {
   printf("file is not .bmp file!");
   
   return;
  }
  temp_DWORD=(DWORD *)(BmpFileHeader+sizeof(bitHead.bfType));
  bitHead.bfSize=*temp_DWORD;
  temp_WORD=(WORD*)(BmpFileHeader+sizeof(bitHead.bfType)+sizeof(bitHead.bfSize));
  bitHead.bfReserved1=*temp_WORD;
  temp_WORD=(WORD*)(BmpFileHeader+sizeof(bitHead.bfType)+sizeof(bitHead.bfSize)+sizeof(bitHead.bfReserved1));
  bitHead.bfReserved2=*temp_WORD;
  temp_DWORD=(DWORD*)(BmpFileHeader+sizeof(bitHead.bfType)+sizeof(bitHead.bfSize)+sizeof(bitHead.bfReserved1)+sizeof(bitHead.bfReserved2));
  bitHead.bfOffBits=*temp_DWORD;
 
  
  
  showBmpHead(&bitHead);
  printf("\n\n");
 
  //读取位图信息头信息
  fread(&bitInfoHead,1,sizeof(BITMAPINFOHEADER),pfile);
  showBmpInforHead(&bitInfoHead);
  printf("\n");
  
 }
 else
 {
  printf("file open fail!\n");
  return;
 }
 
 tagRGBQUAD *pRgb ;

 if(bitInfoHead.biBitCount < 24)//有调色板
 {
  //读取调色盘结信息
  long nPlantNum = long(pow(2,double(bitInfoHead.biBitCount))); // Mix color Plant Number;
  pRgb=(tagRGBQUAD *)malloc(nPlantNum*sizeof(tagRGBQUAD));
  memset(pRgb,0,nPlantNum*sizeof(tagRGBQUAD));
  int num = fread(pRgb,4,nPlantNum,pfile);

  printf("Color Plate Number: %d\n",nPlantNum);

  printf("颜色板信息:\n");
  for (int i =0; i<nplantnum;i++)
  {
   if (i%5==0)
   {
    printf("\n");
   }
   showRgbQuan(&pRgb[i]);

  }

  printf("\n");

 }


 int width = bitInfoHead.biWidth;
 int height = bitInfoHead.biHeight;
 //分配内存空间把源图存入内存
 int l_width = WIDTHBYTES(width* bitInfoHead.biBitCount);//计算位图的实际宽度并确保它为32的倍数
 BYTE *pColorData=(BYTE *)malloc(height*l_width);
 memset(pColorData,0,height*l_width);
 long nData = height*l_width;

 //把位图数据信息读到数组里
 fread(pColorData,1,nData,pfile);

 

 //将位图数据转化为RGB数据
 tagRGBQUAD* dataOfBmp;
 dataOfBmp = (tagRGBQUAD *)malloc(width*height*sizeof(tagRGBQUAD));//用于保存各像素对应的RGB数据
 memset(dataOfBmp,0,width*height*sizeof(tagRGBQUAD));

 if(bitInfoHead.biBitCount<24)//有调色板,即位图为非真彩色
 {
  int k;
  int index = 0;
  if (bitInfoHead.biBitCount == 1)
  {
   for(int i=0;i<height;i++)
    for(int j=0;j<width;j++)
    {
     BYTE mixIndex= 0;
     k = i*l_width + j/8;//k:取得该像素颜色数据在实际数据数组中的序号
     //j:提取当前像素的颜色的具体值
     mixIndex = pColorData[k];
     switch(j%8)
     {
     case 0:
      mixIndex = mixIndex<<7;
      mixIndex = mixIndex>>7;
      break;
     case 1:
      mixIndex = mixIndex<<6;
      mixIndex = mixIndex>>7;
      break;
     case 2:
      mixIndex = mixIndex<<5;
      mixIndex = mixIndex>>7;
      break;

     case 3:
      mixIndex = mixIndex<<4;
      mixIndex = mixIndex>>7;
      break;
     case 4:
      mixIndex = mixIndex<<3;
      mixIndex = mixIndex>>7;
      break;

     case 5:
      mixIndex = mixIndex<<2;
      mixIndex = mixIndex>>7;
      break;
     case 6:
      mixIndex = mixIndex<<1;
      mixIndex = mixIndex>>7;
      break;

     case 7:
      mixIndex = mixIndex>>7;
      break;
     }

     //将像素数据保存到数组中对应的位置
     dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
     dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
     dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
     dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
     index++;

    }
  }

  if(bitInfoHead.biBitCount==2)
  {
   for(int i=0;i<height;i++)
    for(int j=0;j<width;j++)
    {
     BYTE mixIndex= 0;
     k = i*l_width + j/4;//k:取得该像素颜色数据在实际数据数组中的序号
     //j:提取当前像素的颜色的具体值
     mixIndex = pColorData[k];
     switch(j%4)
     {
     case 0:
      mixIndex = mixIndex<<6;
      mixIndex = mixIndex>>6;
      break;
     case 1:
      mixIndex = mixIndex<<4;
      mixIndex = mixIndex>>6;
      break;
     case 2:
      mixIndex = mixIndex<<2;
      mixIndex = mixIndex>>6;
      break;
     case 3:
      mixIndex = mixIndex>>6;
      break;
     }

     //将像素数据保存到数组中对应的位置
     dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
     dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
     dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
     dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
     index++;


    }
  }
  if(bitInfoHead.biBitCount == 4)
  {
   for(int i=0;i<height;i++)
    for(int j=0;j<width;j++)
    {
     BYTE mixIndex= 0;
     k = i*l_width + j/2;
     mixIndex = pColorData[k];
     if(j%2==0)
     {//低
      mixIndex = mixIndex<<4;
      mixIndex = mixIndex>>4;
     }
     else
     {//高
      mixIndex = mixIndex>>4;
     }

     dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
     dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
     dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
     dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
     index++;

    }

  }
  if(bitInfoHead.biBitCount == 8)
  {
   for(int i=0;i<height;i++)
    for(int j=0;j<width;j++)
    {
     BYTE mixIndex= 0;

     k = i*l_width + j;

     mixIndex = pColorData[k];

     dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
     dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
     dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
     dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
     index++;

 

    }
  }
  if(bitInfoHead.biBitCount == 16)
  {
   for(int i=0;i<height;i++)
    for(int j=0;j<width;j++)
    {
     WORD mixIndex= 0;

     k = i*l_width + j*2;
     WORD shortTemp;
     shortTemp = pColorData[k+1];
     shortTemp = shortTemp<<8;

     mixIndex = pColorData[k] + shortTemp;

     dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;
     dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;
     dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;
     dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;
     index++;
    }
  }
 }
 else//位图为24位真彩色
 {
  int k;
  int index = 0;
  for(int i=0;i<height;i++)
   for(int j=0;j<width;j++)
   {
    k = i*l_width + j*3;
    dataOfBmp[index].rgbRed = pColorData[k+2];
    dataOfBmp[index].rgbGreen = pColorData[k+1];
    dataOfBmp[index].rgbBlue = pColorData[k];
    index++;
   }
 }


 printf("像素数据信息:\n");
/*
 for (int i=0; i<width*height; i++)
 {
  if (i%5==0)
  {
   printf("\n");
  }
  showRgbQuan(&dataOfBmp[i]);
 }
*/
 fclose(pfile);
 
 if (bitInfoHead.biBitCount<24)
 {
  free(pRgb);
 }

 free(dataOfBmp);
 free(pColorData);
   free(BmpFileHeader);
 printf("\n");

} 

相关文章推荐

使用ffmpeg将BMP图片编码为x264视频文件,将H264视频保存为BMP图片,yuv视频文件保存为图片的代码

ffmpeg开源库,实现将bmp格式的图片编码成x264文件。参考资料:http://blog.csdn.net/eightdegree/article/details/7425635#reply  ...

BMP文件读取

读取BMP位图结构信息

BMP图像的读取和生成

1. BMP.h#ifndef _BMP_H_ #define _BMP_H_ #ifdef __cplusplus extern "C" { #endif typedef enum _BI...

给图像数据加上BMP文件头

需要将摄像头采集到的图像数据

详细的BMP文件格式解析

  • 2014年12月23日 23:11
  • 48KB
  • 下载

BMP文件格式解析

  • 2014年04月21日 22:52
  • 360KB
  • 下载

bmp文件格式的解析与显示

在各种图片文件格式中,bmp格式的图片应该是最普及的和最简单的,当然这是从技术上讲。网络上也有很多这方面的文章,但是给出的代码与开发环境联系都联系紧密,或者只是原理性的。有关bmp文件格式大家可以百度...

bmp文件格式解析

  • 2012年07月29日 17:49
  • 163KB
  • 下载

2,BMP文件格式解析

BMP图像 42 4D 5A 00 00 00 00 00 00 00 36 00 00 00 28 00 00 00 03 00 00 00 03 00 00 00 01 00 18 00 00...
  • maxhn0
  • maxhn0
  • 2016年08月31日 15:38
  • 557

详细解析DXF文件格式

  • 2009年08月02日 19:33
  • 8KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:bmp文件格式详细解析
举报原因:
原因补充:

(最多只允许输入30个字)