C语言 BMP图片处理

http://feng88724.iteye.com/blog/316651

 

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. }  

 

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值