zoukankan      html  css  js  c++  java
  • 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位图像基本上没有颜色区别。


  • 相关阅读:
    隐藏NGINX服务器名称 和版本号
    salt-grains
    格式化输出文本的方法
    递归例子
    yield 生成器例子
    Python基础之函数
    Python基础之面向对象
    Python基础之模块2
    Python基础之字符编码
    Python基础之文件操作
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/6933729.html
Copyright © 2011-2022 走看看