BMP文件转YUV文件
前言
BMP是 Windows 操作系统中的标准图像文件格式,其分成两类:设备相关位图(DDB)和设备无关位图(DIB)。它采用位映射存储格式,除了图像深度可选以外,在绝大多数应用中不采用其他任何压缩,因此,BMP 文件所占用的空间很大。
BMP 文件的图像深度可选 lbit、4bit、8bit、16bit 及 24bit。BMP 文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于 BMP 文件格式是 Windows 环境中交换与图有关的数据的一种标准,因此在 Windows 环境中运行的图形图像软件都支持 BMP 图像格式。
典型BMP图像文件组成的四部分:
(1)位图头文件数据结构,它包含 BMP 图像文件的类型、显示内容等信息。
(2)位图信息数据结构,它包含有 BMP 图像的宽、高、压缩方法,以及定义颜色等信息。
(3)调色板,这个部分具有可选择性,有些位图需要调色板,有些位图,比如真彩色图(24位的 BMP)就不需要调色板。
(4)位图数据,这部分的内容根据 BMP 位图使用的位数不同而不同,在 24 位图中直接使用 RGB,而其他的小于 24 位的使用调色板中颜色索引值。
以下为相应的数据结构:
实验步骤:
1.制作BMP文件:
我选择的是制作真彩色图24位的BMP文件,使用画板与Photoshop制作了5张720*576的24位BMP文件图像。分别如下:
代码如下(示例):
2.编写程序,实现文件转换
思路:
1,参考老师给的读取头文件的代码读取位图文件头与位图信息头。
2,开辟rgb yuv缓冲区,读取BMP文件数据,由于选择的是24位文件,其图像数据区存储的内容就是R G B的真值,就可以直接读取了。
3,调用rgb2yuv的函数,将其最终转换成yuv文件。
4,每个画面出现50帧,yuv文件中一共出现5种不同的画面,写入两个for循环,在命令栏设置相应参数。
主函数代码如下:
main.cpp
#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "rgb2yuv.h"
using namespace std;
int main(int argc, char** argv)
{
BITMAPFILEHEADER File_header;
BITMAPINFOHEADER Info_header;
char* yuvFileName = NULL;
char* bmpFileName = NULL;
int zhen=50;
zhen=atoi(argv[7]);
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");
//读取位图文件头
if(fread(&File_header,sizeof(BITMAPFILEHEADER),1,bmpFile)!=1)
{
printf("erro!");
exit(0);
}
if(File_header.bfType != 0x4D42)
{
printf("no bmp!");
exit(0);}
else
{printf("this is a bmp file!");}
//读取位图信息头
if(fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmpFile)!=1)
{printf("erro!");
exit(0);}
unsigned int frameWidth,frameHeight;
frameWidth=Info_header.biWidth;
frameHeight=Info_header.biHeight;
//开辟rgbyuv缓冲区
rgbBuf = (unsigned char*)malloc( frameWidth * frameHeight * 3);//输入的rgb文件的数组缓冲区
yBuf = (unsigned char*)malloc(frameWidth * frameHeight);
uBuf = (unsigned char*)malloc((frameWidth * frameHeight) / 4);
vBuf = (unsigned char*)malloc((frameWidth * frameHeight) / 4);
fread(rgbBuf, 1, frameWidth * frameHeight*3, bmpFile);//读图像数据
if(!RGB2YUV(frameWidth ,frameHeight, rgbBuf, yBuf, uBuf, vBuf, flip))
{
for(int j=0;j<zhen;j++)
{
fwrite(yBuf, 1, frameWidth * frameHeight, yuvFile);
fwrite(uBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
fwrite(vBuf, 1, (frameWidth * frameHeight) / 4, yuvFile);
}
}
}
fclose(bmpFile);
fclose(yuvFile);
return(0);
}
RGB2YUV.CPP
#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;
}
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;
}
实验结果:
总结
1,命令行的使用使程序变得更加简洁方便。
2,做文件转换时必须事先清楚文件构成,BMP文件每像素所占的比特数不同其存储的规则也不同,处理方法当然也就不同。
3,用结构体读BMP文件的各部分信息非常方便。