BMP文件格式,RGB之间格式转换 碰到坑,MARK

Posted winafa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BMP文件格式,RGB之间格式转换 碰到坑,MARK相关的知识,希望对你有一定的参考价值。

很多人在转储bmp文件的时候,会出现各种各样的问题,特别是抓屏的时候,经常保存下来的图片 怪怪的,偏差很大!比如下面:

技术图片

 

 有时候,明明感觉自己是对的,但往往结果很让人抓狂。

这种情况一般是对bmp文件格式理解不对,或者没有透彻导致,当然至少是显示出来,所以大部分是对的,只是某些地方出错!

网上也有很多bmp文件格式,但都说得不够透彻,导致实际总要走些弯路。

 

bmp是常见图片格式,使用非常广泛。近期在处理ui库的时候,了解下bmp格式,也发现其中一些坑,记录下。

bmp格式很简单,网上搜索一堆,百科上也有介绍,这里就不重复介绍,

最重要就是文件头,主要是 前54字节,可以用UE看看 bmp:
技术图片

 

 

注意:

1. 红色框里的内容,是重点

2. 表示数值的,都是低在前,包括后面的像素点。例如,RGB888 实际存放顺序是 BGR888

 

颜色:有几种标准,RGB是常见一种,也非常好理解,所以下面重点是RGB颜色标准。

 

由于4色,黑白色,16色,256色,色差失真太大,除了特殊场合,现在很少用了,因此,我也不怎么去尝试。

对于256色,其实存在画板的概念,可以提升图片效果,降低存储空间,有兴趣可以去了解下

 

重点说下:16位色 24位色

注意区别:16色与16位色,16色就是16种颜色,非常单调;16位色,就是用16bit 来表示颜色,有65536种颜色,比较丰富。

24位色就是24bit来表示颜色,那么有256*256*256种颜色,那么颜色更丰富,满足大部分的正常使用。

        既然24位色这么好,为什么还有用16位色呢?甚至更低色?主要场合不一样,位数越少,表现效果当然越差了,但存储占空间就越少,意味着处理就更快,在显示要求并不是那么高的地方,还是很有市场的,另外颜色也受到显示对象限制,有年代的人应该知道以前屏幕是单色的,因此再多的色彩最终还是 0或1;后来出现彩色屏幕,但颜色也很少,就那么几种,但比单色效果好很多了;再后来出现 真彩色,才使得我们进入888的时代,当然以后更多,颜色也更绚丽。总之,颜色取决于你的设备/处理能力/硬件成本。

 

前面说到的 RGB888就是 24位色=8+8+8

那么16位色又是?RGB565=5+6+5,还有RGB555=5+5+5 ,一般RGB565比较多。

 

“在我们的计算机中图像是以RGB888格式显示图像的,24位图每个像素保存了32bit的数据,即RGB888+Alpha,Alpha就是半透明填充字节……”  但设备当中,我们经常用RGB565,因此两者之间是要转换的,转换算法,网上很多,也很简单,这里就不重复描述。

 

当然实际不会这么简单,我就碰到这么一个坑,通过坑,就好好理解BMP格式。

不知道大家有没有注意到bmp格式里的 001EH 这里的含义: 

  •            0 - 不压缩 (使用BI_RGB表示)
  •       1 - RLE 8-使用8位RLE压缩方式(用BI_RLE8表示)
  •       2 - RLE 4-使用4位RLE压缩方式(用BI_RLE4表示)
  •       3 - Bitfields-位域存放方式(用BI_BITFIELDS表示)   一般用于bit12以及bit32,这里是 重点!重点!重点!重点!

 

因为目标文件是RGB565,16bit,所以 001E这里要填 3,注意不能填0!注意不能填0 !注意不能填0!

那么填了3是否就OK了呢?不是!因为16bit,还有RGB565 以及 RGB555两种标准,你还要告诉对方用哪种格式!不然系统就傻了!

那么怎么告诉系统呢?需要在 原先54字节 后面增加一些信息,主要增加16字节内容:

内容 长度 填写(注意还是要低在前)
红色占位 4字节 RGB565 要填:0x0000F800
绿色占位 4字节 RGB565 要填:0x000007E0
蓝色占位 4字节 RGB565 要填:0x0000001F
预留 4字节 填00

 

注意:

由于头信息变成 70字节了,因此 偏移量/长度  地方的值也要随着改变

还有一个地方:000EH 这个地方要增加16,这里要填 0x38 !

 

     ps:由此可见,如果是RGB555的话,这些地方也要改

可以看下实际51*51的头,用UE:

 技术图片

 

还有一个坑,就是bmp要4字节对齐,本来是很好理解的,但实际却是

“每一行都得4字节对齐,不足的用 00H 补齐”

举个例子:

如果图片是 51 * 51  格式: RGB565(一个像素点为2字节)

每行字节数=51*2=102字节,102不是4倍数,所以要补偿两个00,使之=104字节。

实际文件长度一共补偿了102字节。

 技术图片

 

 

下面给一个我创建bmp头函数,就可以实现自己的bmp头,由于平时就用到RGB888 跟 RGB565 两种,所以没考虑其他类型的,如果有兴趣可以完善以下代码,使之更加通用!

  1 //pTitle 固定为70字节, 创建
  2 //byBits 位数,比如16 24
  3 //nWidth 图片宽度(多少像素点)
  4 //nHeight 图片高度(多少像素点)
  5 //nFileLen 图片总像素点 = nWidth * nHeight
  6 void CreateBmpTitle(byte *pTitle,byte byBits,UINT nWidth,UINT nHeight,UINT nFileLen)
  7 {
  8     int nPos = 0;
  9     memset(pTitle,0,70);
 10  
 11     //每个像素占多少字节
 12     byte bmpBytes = 2;
 13     if(byBits <= 8)
 14     {
 15         bmpBytes = 1;
 16     }
 17     else if(byBits <= 16)
 18     {
 19         bmpBytes = 2;
 20     }
 21     else if(byBits <= 24)
 22     {
 23         bmpBytes = 3;
 24     }
 25     else
 26     {
 27         bmpBytes = 4;
 28     }
 29  
 30     //每一行都要凑齐4
 31     UINT nLineLen = nWidth * bmpBytes;
 32     if((nLineLen % 4) != 0)
 33     {
 34         UINT nAdd = (4 - (nLineLen % 4)) * nHeight;
 35         nFileLen += nAdd;
 36     }
 37  
 38     pTitle[nPos++] = B;
 39     pTitle[nPos++] = M;
 40  
 41     //文件长度
 42     SetUINT_L(nFileLen+70,pTitle,nPos);
 43     nPos += 8;//预留4也跳过
 44  
 45     //偏移量
 46     SetUINT_L(70,pTitle,nPos);
 47     nPos += 4;
 48  
 49     //windows, 56
 50     SetUINT_L(56,pTitle,nPos);
 51     nPos += 4;
 52  
 53     //宽度
 54     SetUINT_L(nWidth,pTitle,nPos);
 55     nPos += 4;
 56     //高度
 57     SetUINT_L(nHeight,pTitle,nPos);
 58     nPos += 4;
 59     //平面数=1
 60     SetUSHORT_L(1,pTitle,nPos);
 61     nPos += 2;
 62  
 63     //bits 数
 64     SetUSHORT_L((USHORT)byBits,pTitle,nPos);    
 65     nPos += 2;
 66  
 67     if(byBits == 24)
 68     {
 69         nPos += 4;
 70     }
 71     else if((byBits == 16) || (byBits == 15) || (byBits == 32))
 72     {
 73         //BI_BITFIELDS
 74         SetUINT_L(3,pTitle,nPos);
 75         nPos += 4;
 76     }
 77  
 78     //内容长度,必须为4倍数
 79     SetUINT_L(nFileLen,pTitle,nPos);
 80     nPos += 4;
 81  
 82     SetUINT_L(0xb12,pTitle,nPos);
 83     nPos += 4;
 84  
 85     SetUINT_L(0xb12,pTitle,nPos);
 86     nPos += 4;
 87  
 88     if(byBits == 16)
 89     {
 90         // 565 分别表示 RGB 所在bit位数
 91         nPos = 54;
 92  
 93         SetUINT_L(0xF800,pTitle,nPos);
 94         nPos += 4;
 95  
 96         SetUINT_L(0x07E0,pTitle,nPos);
 97         nPos += 4;
 98  
 99         SetUINT_L(0x001F,pTitle,nPos);
100         nPos += 4;
101     }
102  
103  
104     ///其他都是0
105 }

 

以上是关于BMP文件格式,RGB之间格式转换 碰到坑,MARK的主要内容,如果未能解决你的问题,请参考以下文章

16位bmp文件中RGB555转RGB565算法

java 如何进行 bmp格式的图片转换成jpg格式

从 24bpp 位图中获取每个像素的 RGB 值,以便在 C 中转换为 GBA 格式

C语言实现RGB888转BMP格式图片功能

如何用MATLAB把bmp格式变成灰度图像

C语言实现BMP格式转RGB格式YUV格式