C语言 BMP图片处理

     BMP是bitmap的缩写形式,bitmap顾名思义,就是位图也即Windows位图。它一般由4部分组成:文件头信息块、图像描述信息块、颜色表(在真彩色模式无颜色表)和图像数据区组成。在系统中以BMP为扩展名保存。  
    打开Windows的画图程序,在保存图像时,可以看到三个选项:2色位图(黑白)、16色位图、256色位图和24位位图。这是最普通的生成位图的工具,在这里讲解的BMP位图形式,主要就是指用画图生成的位图(当然,也可以用其它工具软件生成)。  
    现在讲解BMP的4个组成部分:  
   
  1.文件头信息块  
   
  0000-0001:文件标识,为字母ASCII码“BM”。  
  0002-0005:文件大小。  
  0006-0009:保留,每字节以“00”填写。  
  000A-000D:记录图像数据区的起始位置。各字节的信息依次含义为:文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01)。  
   
   
  2.图像描述信息块  
   
  000E-0011:图像描述信息块的大小,常为28H。  
  0012-0015:图像宽度。  
  0016-0019:图像高度。  
  001A-001B:图像的plane(平面?)总数(恒为1)。  
   
  001C-001D:记录像素的位数,很重要的数值,图像的颜色数由该值决定。  
  001E-0021:数据压缩方式(数值位0:不压缩;1:8位压缩;2:4位压缩)。  
  0022-0025:图像区数据的大小。  
  0026-0029:水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。  
  002A-002D:垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。  
  002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。  
   
  3.颜色表  
   
    颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(像素的透明度值,一般不需要)。即首先4字节表示颜色号0的颜色,接下来表示颜色号1的颜色,依此类推。  
   
  4.图像数据区  
   
    颜色表接下来位为位图文件的图像数据区,在此部分记录着每点像素对应的颜色号,其记录方式也随颜色模式而定,既2色图像每点占1位(8位为1字节);16色图像每点占4位(半字节);256色图像每点占8位(1字节);真彩色图像每点占24位(3字节)。所以,整个数据区的大小也会随之变化。究其规律而言,可的出如下计算公式:图像数据信息大小=(图像宽度*图像高度*记录像素的位数)/8。  
   
  然而,未压缩的图像信息区的大小。除了真彩色模式外,其余的均大于或等于数据信息的大小。这是为什么呢?原因有两个:  
    1.BMP文件记录一行图像是以字节为单位的。因此,就不存在一个字节中的数据位信息表示的点在不同的两行中。也就是说,设显示模式位16色,在每个字节分配两个点信息时,如果图像的宽度位奇数,那么最后一个像素点的信息将独占一个字节,这个字节的后4位将没有意义。接下来的一个字节将开始记录下一行的信息。  
    2.为了显示的方便,除了真彩色外,其他的每中颜色模式的行字节数要用数据“00”补齐为4的整数倍。如果显示模式为16色,当图像宽为19时,存储时每行则要补充4-(19/2+1)%4=2个字节(加1是因为里面有一个像素点要独占了一字节)。如果显示模式为256色,当图像宽为19时,每行也要补充4-19%4=1个字节。  
    还有一点我要申明,当屏幕初始化为16或256色模式时,一定要设置调色板或修正颜色值,否则无法得到正确的图像颜色。

C代码 复制代码
  1. //ReadBitMap   
  2. //   
  3. #include <string.h>    
  4. #include <math.h>      
  5. #include <stdio.h>      
  6. #include <stdlib.h>      
  7. #include <malloc.h>   
  8.   
  9.   
  10. #define   WIDTHBYTES(bits) (((bits)+31)/32*4)   
  11.   
  12. typedef unsigned char BYTE;   
  13. typedef unsigned short WORD;   
  14. typedef unsigned long DWORD;   
  15. typedef long LONG;   
  16.   
  17.   
  18. //位图文件头信息结构定义   
  19. //其中不包含文件类型信息(由于结构体的内存结构决定,要是加了的话将不能正确读取文件信息)   
  20.   
  21. typedef struct tagBITMAPFILEHEADER {   
  22.   
  23. DWORD bfSize;           //文件大小   
  24. WORD   bfReserved1;     //保留字,不考虑   
  25. WORD   bfReserved2;     //保留字,同上   
  26. DWORD bfOffBits;        //实际位图数据的偏移字节数,即前三个部分长度之和   
  27. } BITMAPFILEHEADER;    
  28.   
  29.   
  30. //信息头BITMAPINFOHEADER,也是一个结构,其定义如下:   
  31.   
  32. typedef struct tagBITMAPINFOHEADER{   
  33. //public:   
  34. DWORD   biSize;             //指定此结构体的长度,为40   
  35. LONG    biWidth;            //位图宽   
  36. LONG    biHeight;           //位图高   
  37. WORD    biPlanes;           //平面数,为1   
  38. WORD    biBitCount;         //采用颜色位数,可以是1,2,4,8,16,24,新的可以是32   
  39. DWORD   biCompression;      //压缩方式,可以是0,1,2,其中0表示不压缩   
  40. DWORD   biSizeImage;        //实际位图数据占用的字节数   
  41. LONG    biXPelsPerMeter;    //X方向分辨率   
  42. LONG    biYPelsPerMeter;    //Y方向分辨率   
  43. DWORD   biClrUsed;          //使用的颜色数,如果为0,则表示默认值(2^颜色位数)   
  44. DWORD   biClrImportant;     //重要颜色数,如果为0,则表示所有颜色都是重要的   
  45. } BITMAPINFOHEADER;    
  46.   
  47.   
  48. //调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。24位和32位是不需要调色板的。   
  49. //(似乎是调色板结构体个数等于使用的颜色数。)   
  50.   
  51. typedef struct tagRGBQUAD {    
  52. //public:   
  53. BYTE     rgbBlue; //该颜色的蓝色分量   
  54. BYTE     rgbGreen; //该颜色的绿色分量   
  55. BYTE     rgbRed; //该颜色的红色分量   
  56. BYTE     rgbReserved; //保留值   
  57. } RGBQUAD;   
  58.   
  59.   
  60.   
  61. void showBmpHead(BITMAPFILEHEADER* pBmpHead)   
  62. {   
  63. printf("位图文件头:\n");   
  64. printf("文件大小:%d\n",pBmpHead->bfSize);   
  65. printf("保留字:%d\n",pBmpHead->bfReserved1);   
  66. printf("保留字:%d\n",pBmpHead->bfReserved2);   
  67. printf("实际位图数据的偏移字节数:%d\n",pBmpHead->bfOffBits);   
  68.   
  69. }   
  70.   
  71.   
  72. void showBmpInforHead(tagBITMAPINFOHEADER* pBmpInforHead)   
  73. {   
  74. printf("位图信息头:\n");   
  75. printf("结构体的长度:%d\n",pBmpInforHead->biSize);   
  76. printf("位图宽:%d\n",pBmpInforHead->biWidth);   
  77. printf("位图高:%d\n",pBmpInforHead->biHeight);   
  78. printf("biPlanes平面数:%d\n",pBmpInforHead->biPlanes);   
  79. printf("biBitCount采用颜色位数:%d\n",pBmpInforHead->biBitCount);   
  80. printf("压缩方式:%d\n",pBmpInforHead->biCompression);   
  81. printf("biSizeImage实际位图数据占用的字节数:%d\n",pBmpInforHead->biSizeImage);   
  82. printf("X方向分辨率:%d\n",pBmpInforHead->biXPelsPerMeter);   
  83. printf("Y方向分辨率:%d\n",pBmpInforHead->biYPelsPerMeter);   
  84. printf("使用的颜色数:%d\n",pBmpInforHead->biClrUsed);   
  85. printf("重要颜色数:%d\n",pBmpInforHead->biClrImportant);   
  86. }   
  87.   
  88. void showRgbQuan(tagRGBQUAD* pRGB)   
  89. {    
  90. printf("(%-3d,%-3d,%-3d)   ",pRGB->rgbRed,pRGB->rgbGreen,pRGB->rgbBlue);   
  91.   
  92. }   
  93.   
  94.   
  95.   
  96. void main()   
  97. {   
  98.   
  99. BITMAPFILEHEADER   bitHead;   
  100. BITMAPINFOHEADER bitInfoHead;    
  101. FILE* pfile;   
  102.   
  103. char strFile[50];   
  104. printf("please input the .bmp file name:\n");   
  105. scanf("%s",strFile);   
  106.   
  107. pfile = fopen(strFile,"rb");//打开文件   
  108.   
  109. if(pfile!=NULL)   
  110. {   
  111.    printf("file bkwood.bmp open success.\n");   
  112.    //读取位图文件头信息   
  113.    WORD fileType;   
  114.    fread(&fileType,1,sizeof(WORD),pfile);   
  115.    if(fileType != 0x4d42)   
  116.    {   
  117.     printf("file is not .bmp file!");   
  118.     return;   
  119.    }   
  120.    //fseek(pfile,2,SEEK_CUR);   // "BM"   
  121.    fread(&bitHead,1,sizeof(tagBITMAPFILEHEADER),pfile);   
  122.      
  123.    showBmpHead(&bitHead);   
  124.    printf("\n\n");   
  125.   
  126.    //读取位图信息头信息   
  127.    fread(&bitInfoHead,1,sizeof(BITMAPINFOHEADER),pfile);   
  128.    showBmpInforHead(&bitInfoHead);   
  129.    printf("\n");   
  130. }   
  131. else  
  132. {   
  133.    printf("file open fail!\n");   
  134.    return;   
  135. }   
  136.   
  137.   
  138. tagRGBQUAD *pRgb ;   
  139.   
  140. if(bitInfoHead.biBitCount < 24)//有调色板   
  141. {    
  142.    //读取调色盘结信息   
  143.    long nPlantNum = long(pow(2,double(bitInfoHead.biBitCount)));    //   Mix color Plant Number;   
  144.    pRgb=(tagRGBQUAD *)malloc(nPlantNum*sizeof(tagRGBQUAD));    
  145.    memset(pRgb,0,nPlantNum*sizeof(tagRGBQUAD));   
  146.    int num = fread(pRgb,4,nPlantNum,pfile);   
  147.      
  148.    printf("Color Plate Number: %d\n",nPlantNum);   
  149.   
  150.    printf("颜色板信息:\n");   
  151.    for (int i =0; i<nPlantNum;i++)   
  152.    {   
  153.     if (i%5==0)   
  154.     {   
  155.      printf("\n");   
  156.     }   
  157.     showRgbQuan(&pRgb[i]);   
  158.       
  159.    }   
  160.   
  161.    printf("\n");   
  162.      
  163. }   
  164.   
  165.   
  166. int width = bitInfoHead.biWidth;   
  167. int height = bitInfoHead.biHeight;   
  168. //分配内存空间把源图存入内存      
  169. int l_width   = WIDTHBYTES(width* bitInfoHead.biBitCount);//计算位图的实际宽度并确保它为32的倍数   
  170. BYTE    *pColorData=(BYTE *)malloc(height*l_width);      
  171. memset(pColorData,0,height*l_width);      
  172. long nData = height*l_width;   
  173.   
  174. //把位图数据信息读到数组里      
  175. fread(pColorData,1,nData,pfile);      
  176.        
  177.   
  178.   
  179. //将位图数据转化为RGB数据   
  180. tagRGBQUAD* dataOfBmp;   
  181. dataOfBmp = (tagRGBQUAD *)malloc(width*height*sizeof(tagRGBQUAD));//用于保存各像素对应的RGB数据   
  182. memset(dataOfBmp,0,width*height*sizeof(tagRGBQUAD));   
  183.   
  184. if(bitInfoHead.biBitCount<24)//有调色板,即位图为非真彩色    
  185. {   
  186.    int k;   
  187.    int index = 0;   
  188.    if (bitInfoHead.biBitCount == 1)   
  189.    {   
  190.     for(int i=0;i<height;i++)   
  191.      for(int j=0;j<width;j++)   
  192.      {   
  193.       BYTE mixIndex= 0;   
  194.       k = i*l_width + j/8;//k:取得该像素颜色数据在实际数据数组中的序号   
  195.       //j:提取当前像素的颜色的具体值       
  196.       mixIndex = pColorData[k];   
  197.       switch(j%8)   
  198.       {   
  199.       case 0:   
  200.        mixIndex = mixIndex<<7;   
  201.        mixIndex = mixIndex>>7;   
  202.        break;   
  203.       case 1:   
  204.        mixIndex = mixIndex<<6;   
  205.        mixIndex = mixIndex>>7;   
  206.        break;   
  207.       case 2:   
  208.        mixIndex = mixIndex<<5;   
  209.        mixIndex = mixIndex>>7;   
  210.        break;   
  211.   
  212.       case 3:   
  213.        mixIndex = mixIndex<<4;   
  214.        mixIndex = mixIndex>>7;   
  215.        break;   
  216.       case 4:   
  217.        mixIndex = mixIndex<<3;   
  218.        mixIndex = mixIndex>>7;   
  219.        break;   
  220.   
  221.       case 5:   
  222.        mixIndex = mixIndex<<2;   
  223.        mixIndex = mixIndex>>7;   
  224.        break;   
  225.       case 6:   
  226.        mixIndex = mixIndex<<1;   
  227.        mixIndex = mixIndex>>7;   
  228.        break;   
  229.   
  230.       case 7:   
  231.        mixIndex = mixIndex>>7;   
  232.        break;   
  233.       }   
  234.   
  235.       //将像素数据保存到数组中对应的位置   
  236.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;   
  237.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;   
  238.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;   
  239.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;   
  240.       index++;   
  241.   
  242.     }   
  243.    }   
  244.   
  245.    if(bitInfoHead.biBitCount==2)   
  246.    {   
  247.     for(int i=0;i<height;i++)   
  248.      for(int j=0;j<width;j++)   
  249.      {   
  250.       BYTE mixIndex= 0;   
  251.       k = i*l_width + j/4;//k:取得该像素颜色数据在实际数据数组中的序号   
  252.       //j:提取当前像素的颜色的具体值       
  253.       mixIndex = pColorData[k];   
  254.       switch(j%4)   
  255.       {   
  256.       case 0:   
  257.        mixIndex = mixIndex<<6;   
  258.        mixIndex = mixIndex>>6;   
  259.        break;   
  260.       case 1:   
  261.        mixIndex = mixIndex<<4;   
  262.        mixIndex = mixIndex>>6;   
  263.        break;   
  264.       case 2:   
  265.        mixIndex = mixIndex<<2;   
  266.        mixIndex = mixIndex>>6;   
  267.        break;   
  268.       case 3:   
  269.        mixIndex = mixIndex>>6;   
  270.        break;   
  271.       }   
  272.   
  273.       //将像素数据保存到数组中对应的位置   
  274.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;   
  275.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;   
  276.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;   
  277.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;   
  278.       index++;   
  279.   
  280.   
  281.      }   
  282.    }   
  283.    if(bitInfoHead.biBitCount == 4)   
  284.    {   
  285.     for(int i=0;i<height;i++)   
  286.      for(int j=0;j<width;j++)   
  287.      {   
  288.       BYTE mixIndex= 0;   
  289.       k = i*l_width + j/2;   
  290.       mixIndex = pColorData[k];   
  291.       if(j%2==0)   
  292.       {//低         
  293.        mixIndex = mixIndex<<4;   
  294.        mixIndex = mixIndex>>4;   
  295.       }   
  296.       else  
  297.       {//高   
  298.        mixIndex = mixIndex>>4;   
  299.       }   
  300.   
  301.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;   
  302.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;   
  303.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;   
  304.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;   
  305.       index++;   
  306.   
  307.      }   
  308.   
  309.    }   
  310.    if(bitInfoHead.biBitCount == 8)   
  311.    {   
  312.     for(int i=0;i<height;i++)   
  313.      for(int j=0;j<width;j++)   
  314.      {   
  315.       BYTE mixIndex= 0;   
  316.   
  317.       k = i*l_width + j;   
  318.   
  319.       mixIndex = pColorData[k];   
  320.   
  321.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;   
  322.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;   
  323.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;   
  324.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;   
  325.       index++;   
  326.         
  327.         
  328.   
  329.      }   
  330.    }   
  331.    if(bitInfoHead.biBitCount == 16)   
  332.    {   
  333.     for(int i=0;i<height;i++)   
  334.      for(int j=0;j<width;j++)   
  335.      {   
  336.       WORD mixIndex= 0;   
  337.   
  338.       k = i*l_width + j*2;   
  339.       WORD shortTemp;   
  340.       shortTemp = pColorData[k+1];   
  341.       shortTemp = shortTemp<<8;   
  342.        
  343.       mixIndex = pColorData[k] + shortTemp;   
  344.   
  345.       dataOfBmp[index].rgbRed = pRgb[mixIndex].rgbRed;   
  346.       dataOfBmp[index].rgbGreen = pRgb[mixIndex].rgbGreen;   
  347.       dataOfBmp[index].rgbBlue = pRgb[mixIndex].rgbBlue;   
  348.       dataOfBmp[index].rgbReserved = pRgb[mixIndex].rgbReserved;   
  349.       index++;   
  350.      }   
  351.    }   
  352. }   
  353. else//位图为24位真彩色   
  354. {   
  355.    int k;   
  356.    int index = 0;   
  357.    for(int i=0;i<height;i++)   
  358.     for(int j=0;j<width;j++)   
  359.     {   
  360.      k = i*l_width + j*3;   
  361.      dataOfBmp[index].rgbRed = pColorData[k+2];      
  362.      dataOfBmp[index].rgbGreen = pColorData[k+1];      
  363.      dataOfBmp[index].rgbBlue = pColorData[k];       
  364.      index++;   
  365.     }               
  366. }   
  367.   
  368.   
  369. printf("像素数据信息:\n");   
  370. for (int i=0; i<width*height; i++)   
  371. {   
  372.    if (i%5==0)   
  373.    {   
  374.     printf("\n");   
  375.    }   
  376.    showRgbQuan(&dataOfBmp[i]);   
  377. }   
  378.   
  379. fclose(pfile);    
  380. if (bitInfoHead.biBitCount<24)   
  381. {   
  382.    free(pRgb);   
  383. }   
  384. free(dataOfBmp);   
  385. free(pColorData);   
  386. printf("\n");   
  387.   
  388. }  

posted on 2009-02-20 14:13  Macou  阅读(6144)  评论(5编辑  收藏  举报

导航