bmp文件格式中rgb555与rgb888之间的转换,24位与16位位图的转换
今日,有同事问我。rgb555模式下的位图文件的格式问题,于是花了一下午的时间通过推測与測试,分析出了例如以下bmp文件在rgb555模式下的文件存储规律:
1:bmp文件的文件信息头中的biBitCount数据应该为16
在rgb555模式下,一个像素占用2字节。rgb分别占用5位,另外有一位是填充位。
2:16位数据的组成例如以下
第一个字节:g5g4g3b7b6b5b4b3
第二个字节:0r7r6r5r4r3g7g6
当中第二个字节的左边第一位为填充位。我在实验中用0填充。
3:该16位bmp图像无调色板数据
4:该16位bmp图像在显示时。图片浏览软件(如windows绘图)会将rgb555自己主动转换为rgb888显示。详细的方法例如以下
b7b6b5b4b3->b7b6b5b4b3b7b6b5
r7r6r5r4r3->r7r6r5r4r7r6r5
g7g6g5g4g3->r7r6r5r4g7g6g5
測试图片例如以下
上面的图像为24位bmp图像,从左往右的色带的像素值BGR分别为
0,0,128
0,128,0
128,0,0
0,0,64
0,64,0
64,0,0
32,0,0
假设将图像转换为rgb55模式的bmp文件格式,则转换后的文件为16位的bmp文件。从左往右的色带中的像素值分别为
第一个字节 第二个字节
00000000 01000000
00000000 00000010
00010000 00000000
00000000 00100000
00000000 00000001
00001000 00000000
00000100 00000000
转换代码例如以下:
#include <windows.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <time.h> BYTE *Read24BitBmpFile2Img(const char * filename,int *width,int *height) { FILE * BinFile; BITMAPFILEHEADER FileHeader; BITMAPINFOHEADER BmpHeader; BYTE *img; unsigned int size; int Suc=1,w,h; // Open File *width=*height=0; if((BinFile=fopen(filename,"rb"))==NULL) return NULL; // Read Struct Info if (fread((void *)&FileHeader,1,sizeof(FileHeader),BinFile)!=sizeof(FileHeader)) Suc=-1; if (fread((void *)&BmpHeader,1,sizeof(BmpHeader),BinFile)!=sizeof(BmpHeader)) Suc=-1; if ( (Suc==-1) || (FileHeader.bfOffBits<sizeof(FileHeader)+sizeof(BmpHeader) )) { fclose(BinFile); return NULL; } // Read Image Data *width=w=BmpHeader.biWidth; *height=h=BmpHeader.biHeight; size=(*width)*(*height)*3; fseek(BinFile,FileHeader.bfOffBits,SEEK_SET); if ( (img=new BYTE[size])!=NULL) { for(int i=0;i<h;i++) { if(fread(img+(h-1-i)*w*3,sizeof(BYTE),w*3,BinFile)!=w*3) { fclose(BinFile); delete img; img=NULL; return NULL; } fseek(BinFile,(3*w+3)/4*4-3*w,SEEK_CUR); } } fclose(BinFile); return img; } void BGR8882BGR555(unsigned char *img888, unsigned char * img555,int width,int height) { unsigned char *p = img888; unsigned char *q = img555; unsigned char r,g,b; unsigned char r1,g1,b1; int i ,j; for(i = 0;i< height;i++) { for(j = 0; j< width;j++) { b = *p; g = *(p+1); r = *(p+2); g1 = g>>6; r = (r>>3)<<2; *(q+1) = r | g1; g1 = ((g<<2)>>5)<<5; b1 = (b>>3); *(q) = g1 | b1; p+=3; q+=2; } } } bool Write555BitImg2BmpFile(BYTE *pImg,int width,int height,const char * filename) // 当宽度不是4的倍数时自己主动加入成4的倍数 { FILE *BinFile; BITMAPFILEHEADER FileHeader; BITMAPINFOHEADER BmpHeader; bool Suc=true; int i,extend; BYTE p[4],*pCur; // Open File if((BinFile=fopen(filename,"w+b"))==NULL) { return false; } // Fill the FileHeader FileHeader.bfType= ((WORD) ('M' << 8) | 'B'); FileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); FileHeader.bfSize=FileHeader.bfOffBits+width*height*3L ; FileHeader.bfReserved1=0; FileHeader.bfReserved2=0; if(fwrite((void*)&FileHeader,1,sizeof(BITMAPFILEHEADER),BinFile)!=sizeof(BITMAPFILEHEADER)) Suc=false; // Fill the ImgHeader BmpHeader.biSize = 40; BmpHeader.biWidth = width; BmpHeader.biHeight = height; BmpHeader.biPlanes = 1 ; BmpHeader.biBitCount = 16 ; BmpHeader.biCompression = 0 ; BmpHeader.biSizeImage = 0 ; BmpHeader.biXPelsPerMeter = 0; BmpHeader.biYPelsPerMeter = 0; BmpHeader.biClrUsed = 0; BmpHeader.biClrImportant = 0; if(fwrite((void*)&BmpHeader,1,sizeof(BITMAPINFOHEADER),BinFile)!=sizeof(BITMAPINFOHEADER)) Suc=false; // write image data extend=(2*width+2)/4*4-2*width; if (extend==0) { for(pCur=pImg+(height-1)*2*width;pCur>=pImg;pCur-=2*width) { if (fwrite((void *)pCur,1,width*2,BinFile)!=(unsigned int)(2*width)) Suc=false; // 真实的数据 } } else { for(pCur=pImg+(height-1)*2*width;pCur>=pImg;pCur-=2*width) { if (fwrite((void *)pCur,1,width*2,BinFile)!=(unsigned int)(2*width)) Suc=false; // 真实的数据 if (fwrite((void *)(pCur+2*(width-1)+0),1,extend,BinFile)!=1) Suc=false; // 扩充的数据 } } // return; fclose(BinFile); return Suc; } void main() { int width,height; BYTE *pGryImg=Read24BitBmpFile2Img("test.bmp",&width,&height); printf("%d,%d",width,height); unsigned char *pImg555 = new unsigned char[width*height*2]; BGR8882BGR555(pGryImg, pImg555,width,height); Write555BitImg2BmpFile(pImg555,width,height,"result.bmp"); delete pGryImg; delete pImg555; return; }
执行后生成文件result.bmp文件及其文件属性例如以下
用像素提取工具查看windows绘图打开的result.bmp中各个色条的rgb值例如以下:
以最右边色带为例说明为什么rgb显示为0,0,33
在rgb555中最右边色带的像素值为第一个字节00000100 第二个字节00000000
依据第一个字节:g5g4g3b7b6b5b4b3第二个字节:0r7r6r5r4r3g7g6可知
b7b6b5b4b3为00100
r7r6r5r4r3为00000
g7g6g5g4g3为00000
依照rgb555转为rgb888的转换规则:
b7b6b5b4b3->b7b6b5b4b3b7b6b5
r7r6r5r4r3->r7r6r5r4r7r6r5
g7g6g5g4g3->r7r6r5r4g7g6g5
能够算得rgb888中r的8位数据为00000000,g的8位数据为000000。b的数据为00100001
换算为十进制为RGB(0,0,33)
測试一下自然图像:
原图:24位彩色图像 bmp文件格式
转换后的16位图像(RGB555格式)bmp文件及其文件属性例如以下:
能够看到16位图像与24位图像基本上没有颜色区别。