一.bmp文件介绍
1.简介
BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用广泛。它采用位映射存储格式,除了图像深度可选以外,在绝大多数应用中不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit、16bit及24bit。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。
2.文件格式
BMP(bitmap) 文件由文件头、位图信息头、颜色信息和图 形数据 4 部分组成。文件头主要包含文件的大小、文件类型、 图像数据偏离文件头的长度等信息;位图信息头包含图像的 尺寸信息、图像用几个比特数值来表示一个像素、图像是否 压缩、图像所用的颜色数等信息。颜色信息包含图像所用到 的颜色表,显示图像时需用到几个颜色表来生成调色板,但 如果图像为真彩色,即图像的每个像素用 24 个比特来表示, 文件中就没有这一块信息,也就不需要操作调色板。文件中 的数据块表示图像的相应像素值,需要注意的是:图像的像 素值在文件中的存放顺序为从左到右,从下到上,也就是说, 在 BMP 文件中首先存放的是图像的最后一行像素,最后才存 储图像的第一行像素,但对于同一行的像素,则是按照先左边后右边的顺序存储的;另外一个需要关注的细节是:文件 存储图像的每一行像素值时,如果存储该行像素值所占的字 节数为 4 的倍数,则正常存储;否则,需要在后端补 0,凑足 4 的倍数
2.1位图头文件
//第一部分为位图文件头 BITMAPFILEHEADER,是一个
结构,其定义如下:
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; // 位图文件的类型,必须为 BM
DWORD bfSize; // 位图文件的大小,字节单位
WORD bfReserved1; // 位图文件保留字,必须为 0
WORD bfReserved2; // 位图文件保留字,必须为 0
DWORD bfOffBits; // 位图文件头到数据的偏移
}BITMAPFILEHEADER ;
2.2位图信息头
//第二部分为位图信息头 BITMAPINFOHEADER,也是一
个结构,其定义如下:
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; // 该结构所占字节数
LONG biWidth; // 位图的宽度,像素单位
LONG biHeight; // 位图的高度,像素单位
WORD biPlanes; // 目标设备的位面数,必须为 1
WORD biBitCount; // 颜色深度,即每个像素所占的
位数
DWORD biCompression; // 位图的压缩类型
DWORD biSizeImage; // 位图的大小,字节单位
LONG biXPelsPerMeter; // 位图水平分辨率
LONG biYPelsPerMeter; // 位图的垂直分辨率
DWORD biClrUsed; // 位图实际使用的颜色表中
颜色数
DWORD biClrImportant; // 位图显示中比较重要的颜
色数
} BITMAPINFOHEADER;
2.3调色板
第三部分为调色板 RGBQUAD。当然这是对那些需要调
色板的位图文件而言的。有些位图,如真彩色图,是不需要
调色板的,BITMAPINFOHEADER 后直接是位图数据。
颜色表实际上是一个数组,共有 biClrUsed 个元素(如果
该值为零,则有 2biBitCount 个元素)。数组中每个元素的类
型是一个 RGBQUAD 结构,占 4 个字节,其定义如下:
typedef struct tagRGBQUAD
{
BYTE rgbBlue; // 该颜色的蓝色分量
BYTE rgbGreen; // 该颜色的绿色分量
BYTE rgbRed; // 该颜色的红色分量
BYTE rgbReserved; // 保留值
} RGBQUAD;
位图信息头和颜色表组成位图信息结构 BITMAPINFO, 其定义如下:
typedef struct tagBITMAPINFO
{
BITMAPINFOHEADER bmiHeader; // 位图信息头
RGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO
二.上机实操
大致思路:
main函数
1.打开文件,读取头信息,开辟缓存区
2.将RGB数据写入缓存区
3.调用rgbtoyuv函数转化
4.清理内存,释放缓存区
bmptoyuv函数
1.建立查找表,对像素值进行处理
2.根据位数进行相应处理
函数转化核心
三.代码解析
bmp2rgb
#include#include int bmp2rgb(BITMAPFILEHEADER &filh,BITMAPINFOHEADER &pich,FILE* pFile,unsigned char* rgbbuf) ///读取图像尺寸,以像素数为单位 int w,h,i,j; w=pich.biWidth; h=pich.biHeight; unsigned char* dataBuf=(unsigned char*)malloc(w*h*pich.bit);///创建数据缓冲区 fseek(pFile,filh.bfOffBits,0);///使指针指向对应地方 fread(dataBuf,1,w*h*pich.bit,pFile);///将相应字节大小的数据读入dataBuf bool MakePalette(FILE * pFile,BITMAPFILEHEADER &filh,BITMAPINFOHEADER & pich,RGBQUAD *pRGB_out);申明创建调色板函数 int color;///定义颜色数变量 if(pich.biClrUsed==0) { m=pow(2,pich.8*bit);///若信息头中颜色数为0,则实际颜色数为2的位数次方 } else { color=pich.biClrUsed; } switch(info_h.8*bit)用switch选择语句对不同的位数图像进行不同的操作 { casw 16; for (j = 0; j < h; j++)//j控制行循环 { int pixCnt = 0; for (i = 0; i < w; i+=2)//i控制列循环 { *rgb = (data[i + w*j] & 0x1F) << 3;//B *(rgbbuf + 1) = ((dataBuf[i + w*j] & 0xE0) >> 3) + ((data[i + w*j + 1] & 0x07) << 5); *(rgbbuf+ 2) = dataBuf[i + w*j + 1] & 0xF8; //R rgbbuf += 3; pixCnt++; if (pixCnt == info_h.biWidth) i = w - 1; } } break; case 24: for (j = 0; j < h; j++)//j控制行循环 { int pixCnt = 0; for (i = 0; i < w; i += 3)//i控制列循环 { *rgbbuf = data[i + w*j];//B *(rgbbuf + 1) = data[i + w*j + 1];//G *(rgbbuf + 2) = data[i + w*j + 2];//R rgbbuf += 3; //如果到了行尾补充位则直接转到下一行 pixCnt++; if (pixCnt == info_h.biWidth) i = w - 1; } } break;
RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*color);///开辟调色板区域,RGBQUAD元素数目与所用颜色数相同, for(j=0;j>(8-indext*pich.8*bit)); * rgbbuf = pRGB[index2].rgbBlue; * (rgbbuf+1) = pRGB[index2].rgbGreen; * (rgbbuf+2) = pRGB[index2].rgbRed; if(pic.8*bit == 8) index = 0; else index >>= pic.8*bit; rgbbuf+=3; index2 ++; } } } if(dataBuf!=NULL) { free(dataBuf); } return 0; }
rbgb2yuv定义查找表,节省代码
void InitLookupTable()
{
int i;
for (i = 0; i < 256; i++) RGBYUV02990[i] = (float)0.2990 * i;
for (i = 0; i < 256; i++) RGBYUV05870[i] = (float)0.5870 * i;
for (i = 0; i < 256; i++) RGBYUV01140[i] = (float)0.1140 * i;
for (i = 0; i < 256; i++) RGBYUV01684[i] = (float)0.1684 * i;
for (i = 0; i < 256; i++) RGBYUV03316[i] = (float)0.3316 * i;
for (i = 0; i < 256; i++) RGBYUV04187[i] = (float)0.4187 * i;
for (i = 0; i < 256; i++) RGBYUV00813[i] = (float)0.0813 * i;
}
rgb2yuv
#include "stdlib.h"
#include "rgb2yuv.h"
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
int RGB2YUV (int x_dim, int y_dim, void *bmp, void *y_out, void *u_out, void *v_out, int flip)
{
static int init_done = 0;
long i, j, size;
unsigned char *r, *g, *b;
unsigned char *y, *u, *v;
unsigned char *pu1, *pu2, *pv1, *pv2, *psu, *psv;
unsigned char *y_buffer, *u_buffer, *v_buffer;
unsigned char *sub_u_buf, *sub_v_buf;
if (init_done == 0)
{
InitLookupTable();
init_done = 1;
}
// check to see if x_dim and y_dim are divisible by 2
if ((x_dim % 2) || (y_dim % 2)) return 1;
size = x_dim * y_dim;
// allocate memory
y_buffer = (unsigned char *)y_out;
sub_u_buf = (unsigned char *)u_out;
sub_v_buf = (unsigned char *)v_out;
u_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
v_buffer = (unsigned char *)malloc(size * sizeof(unsigned char));
if (!(u_buffer && v_buffer))
{
if (u_buffer) free(u_buffer);
if (v_buffer) free(v_buffer);
return 2;
}
b = (unsigned char *)bmp;
y = y_buffer;
u = u_buffer;
v = v_buffer;
// convert RGB to YUV
if (!flip) {
for (j = 0; j < y_dim; j ++)
{
y = y_buffer + (y_dim - j - 1) * x_dim;
u = u_buffer + (y_dim - j - 1) * x_dim;
v = v_buffer + (y_dim - j - 1) * x_dim;
for (i = 0; i < x_dim; i ++) {
g = b + 1;
r = b + 2;
*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);
*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 3;
y ++;
u ++;
v ++;
}
}
} else {
for (i = 0; i < size; i++)
{
g = b + 1;
r = b + 2;
*y = (unsigned char)( RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
*u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2 + 128);
*v = (unsigned char)( (*r)/2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
b += 3;
y ++;
u ++;
v ++;
}
}
// subsample UV
for (j = 0; j < y_dim/2; j ++)
{
psu = sub_u_buf + j * x_dim / 2;
psv = sub_v_buf + j * x_dim / 2;
pu1 = u_buffer + 2 * j * x_dim;
pu2 = u_buffer + (2 * j + 1) * x_dim;
pv1 = v_buffer + 2 * j * x_dim;
pv2 = v_buffer + (2 * j + 1) * x_dim;
for (i = 0; i < x_dim/2; i ++)
{
*psu = (*pu1 + *(pu1+1) + *pu2 + *(pu2+1)) / 4;
*psv = (*pv1 + *(pv1+1) + *pv2 + *(pv2+1)) / 4;
psu ++;
psv ++;
pu1 += 2;
pu2 += 2;
pv1 += 2;
pv2 += 2;
}
}
free(u_buffer);
free(v_buffer);
return 0;
}
调色板
bool MakePalette(FILE * pFile,BITMAPFILEHEADER &file_h,BITMAPINFOHEADER & info_h,RGBQUAD *pRGB_out)///定义创建调色板函数
{
int Clr;///定义颜色数变量
if(info_h.biClrUsed==0)
{
Clr=pow(2,info_h.biBitCount);
}
else
{
Clr=info_h.biClrUsed;
}
if ((file_h.bfOffBits - sizeof(BITMAPFILEHEADER) - info_h.biSize) == sizeof(RGBQUAD)*Clr)///判断实际数据偏移量-文件头-信息头
///是否等于调色板大小
{
fseek(pFile,sizeof(BITMAPFILEHEADER)+info_h.biSize,0);///bmpFile文件指针指向调色板处
fread(pRGB_out,sizeof(RGBQUAD),Clr,pFile);//在新的调色板区域写入数据
return true;
}
else
return false;
}
主函数主体代码
定义文件头和信息头,打开五张图片
BITMAPFILEHEADER File_header;
BITMAPINFOHEADER Info_header;
char* yuvFileName = NULL;
char* bmpFileName = NULL;
bool flip = false;
FILE* bmpFile = NULL;
FILE* yuvFile = NULL;
yuvFileName = argv[6];
yuvFile=fopen(yuvFileName,"wb");
unsigned char *rgbBuf, *yBuf, *uBuf,*vBuf;
unsigned char *bmpbuf;
for(int i=1;i<6;i++)
{
bmpFileName = argv[i];
bmpFile = fopen(bmpFileName, "rb");
}
读取文件头,判断是否是bmp类型的
if (fread(&File_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1)
{
printf("read file header error!");
exit(0);
}
if (File_header.bfType != 0x4D42)
{
printf("Not bmp file!");
exit(0);
}
else
{
printf("this is a %cn", File_header.bfType);//debug
}
if (fread(&Info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)
{
printf("read info header error!");
exit(0);
}
else
{
}
int width = Info_header.biWidth; int height = Info_header.biHeight;//读取图像尺寸 unsigned char* rgbBuf=(unsigned char*)malloc(width*height*3); unsigned char* yBuf=(unsigned char*)malloc(width*height); unsigned char* uBuf=(unsigned char*)malloc((width*height)/4); unsigned char* vBuf=(unsigned char*)malloc((width*height)/4);//分配缓存区域
调用文件格式函数
bmp2rgb(File_header,Info_header,bmpFile,rgbBuf); rgb2yuv(width,height,rgbBuf,yBuf,uBuf,vBuf,flip);
读入yuv
for(int k=0;k<30;k++)
{
fwrite(yBuf,1,size,yuv);
fwrite(uBuf,1,size/4,yuv);
fwrite(vBuf,1,size/4,yuv);
}
释放缓存
free(rgbBuf);
free(yBuf);
free(uBuf;
free(vBuf);
fclose(yuv);
四.实验结果
刚好英语pre时看了泰坦尼克号,这次就用泰坦尼克号的照片当例子
上面时五张bmp格式的图片,经过转换播放后



