将多个BMP文件转为一个YUV文件
目录
一、相关概念介绍
1、BMP格式和YUV格式介绍
(1)BMP格式
BMP(Bitmap-File) 位图图像, 亦称为点阵图像,区别于矢量图放大不失真的特征,位图图像是由单个像素点组成,放大后会失真。它是 Windows操作系统中的标准系统中的标准图像,是Windows环境中交换与图有关的数据的一种标准。BMP文件属于自包含文件,包含表格中四个部分。BMP 文件的图像深度可选 lbit、4bit、8bit、16bit及24bit 。
位图文件可看成由4个部分组成:位图文件头(bitmap-file header)、位图信息头(bitmap-information header)、彩色表(color table)和定义位图的字节(位图数据,即图像数据,Data Bits 或Data Body)阵列,它具有如下所示的形式
结构名称 | 符 号 | 功能 |
---|---|---|
位图文件头 (bitmap-file header) | BITMAPFILEHEADER | 包含 BMP图像文件的类型、显示内容等信息 |
位图信息头 (bitmap-information header) | BITMAPINFOHEADER | 包含有BMP图像的宽、高压缩方法,以及定义颜色等信息 |
彩色表 (color table) | Palette | 可选,有些位图需要调色板,有些位图需要调色板,比如真彩(24位的BMP),不需要调色板 |
图像数据阵列字节(BYTE aBitmapBits) | ImageData | 根据位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜索引值 |
(2)YUV格式
YUV分为三个分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。
与我们熟知的RGB类似,YUV也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(U/V)分离,没有U/V信息一样可以显示完整的图像,只不过是黑白的,这样的设计很好地解决了彩色电视机与黑白电视的兼容问题。并且,YUV不像RGB那样要求三个独立的视频信号同时传输,所以用YUV方式传送占用极少的频宽。
YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,Y:U:V=4:4:4,Y:U:V=4:2:2,Y:U:V=4:2:0。
二、实验实现思路
1、实验整体思路
- 打开并读入BMP文件、定义变量,开辟存储空间。BMP文件中首先被读入的应该是文件头和信息头,读入之后才能根据其中给出的关于位图的宽高、bit数等信息进行开辟空间等操作。在本实验中,定义两个指针:BITMAPFILEHEADER File_header; BITMAPINFOHEADER Info_header; 分别用来获取文件头和信息头的数据。注意,这两种类型需要加<windows.h>头文件。
- 提取出RGB数据,写入缓冲区。不同bit的文件不同处理。24bit图可以直接取图片数据;16bit比较复杂,需要取像素数据转换成8bit彩色分量后再写入rgbBuf;8bit,4bit,1bit均需要构造调色板。需要注意的是图像数据需要倒置,具体代码见代码分析部分。
- 调用“RGB2YUV.h”函数实现转换,并写入新的YUV文件。生成视频时可以循环写入,每张图片素材30帧。打开方式“wb+”可以下次继续写入。
- 释放缓冲区,关闭文件。
2、BMP转为RGB
- 读取BMP文件,得到RGB数据:判断是否为BMP文件;判断文件是否可读出;要保证BMP图像的长必须是4的倍数,宽必须是2的倍数
- 通过信息头得到有效位图数据在BMP图像中的位置,进而对有效数据进行读取
- 注意,BMP规定:位图数据自左向右、自下向上依次存放。因此注意。Index_Data是直接从BMP文件中读取的数据倒序存放的buffer,需要变为正序的,即Data
- 得到的Data并不一定是RGB数据,因为BMP有位深的类别。24位BMP,位图有效数据就是RGB,而16位、8位、4位、2位、1位的BMP则需要位操作,从DATA数据中“解析”出RGB数据,具体方法见下图与注释
3、RGB转为YUV色差信号
RGB到色差信号的转换公式如下所示:
Y=0.2990R+0.5870G+0.1140B
R-Y=0.7010R-0.5870G-0.1140B
B-Y=-0.2990R-0.5870G+0.8860B
为了使色差信号的动态范围控制在0.5之间,需要进行归一化,对色差信号引入压缩系数。归一化后的色差信号为:
U=-0.1684R-0.3316G+0.5B
V=0.5R-0.4187G-0.0813B
三、具体程序实现
- bmp2yuv.h
#ifndef BMP2YUV_H_
#define BMP2YUV_H_
//定义掩码组
typedef struct bit32Mask
{
unsigned int rgbRed;
unsigned int rgbGreen;
unsigned int rgbBlue;
unsigned int reserved;
}Mas32;
typedef struct bit16Mask
{
unsigned int rgbRed;
unsigned int rgbGreen;
unsigned int rgbBlue;
}Mas16;
int BMP2RGB32bit(int bitcount, int x_dim, int y_dim, void* bmpbuf, void* rgbbuf, void* mask);
int BMP2RGB24bit(int bitcount, int x_dim, int y_dim, void* bmpbuf, void* rgbbuf);
int BMP2RGB16bit(int bitcount, int x_dim, int y_dim, void* bmpbuf, void* rgbbuf, void* mask);
int BMP2RGBNOT24bit(int bitcount, int x_dim, int y_dim, void* bmpbuf, void* rgbbuf, void* pRGB);
int RGB2YUV(int x_dim, int y_dim, void* bmp, void* y_out, void* u_out, void* v_out, int flip);
void InitLookupTable();
void adjust(unsigned char* b, unsigned char* g, unsigned char* r, unsigned char* y, unsigned char* u, unsigned char* v);
#endif
- RGB2YUV.c
#include "stdlib.h"
#include "bmp2yuv.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;
}
// 检查x_dim 和 y_dim 是否可被2整除
if ((x_dim % 2) || (y_dim % 2)) return 1;
size = x_dim * y_dim;
//分配缓存
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;
//将RGB转为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;
adjust(b, g, r, y, u, v);
b += 3;
y++;
u++;
v++;
}
}
}
else {
for (i = 0; i < size; i++)
{
g = b + 1;
r = b + 2;
adjust(b, g, r, y, u, v);
b += 3;
y++;
u++;
v++;
}
}
// 对 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;
}
void adjust(unsigned char* b, unsigned char* g, unsigned char* r, unsigned char* y, unsigned char* u, unsigned char* v)
{
float temp = 0;
temp = (float)(RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);
temp = temp > 235 ? 235 : temp;
temp = temp < 16 ? 16 : temp;
*y = (unsigned char)temp;
temp = (float)(-RGBYUV01684[*r] - RGBYUV03316[*g] + (*b) / 2 + 128);
temp = temp > 240 ? 240 : temp;
temp = temp < 16 ? 16 : temp;
*u = (unsigned char)temp;
temp = (float)((*r) / 2 - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);
temp = temp > 240 ? 240 : temp;
temp = temp < 16 ? 16 : temp;
*v = (unsigned char)temp;
}
- BMP2RGB.c
#include "stdlib.h"
#include "bmp2yuv.h"
#include <windows.h>
#include <math.h>
int BMP2RGB32bit(int bitcount, int x_dim, int y_dim, void* bmpbuf, void* rgbbuf, void* mask)
{
long i;
unsigned char* bmp, * rgb;
Mas32* mas;
long size = x_dim * y_dim;
bmp = (unsigned char*)bmpbuf;
rgb = (unsigned char*)rgbbuf;
mas = (Mas32*)mask;
if (mask == NULL)
{
for (i = 0; i < size; i++)
{
*(rgb + 0) = *(bmp + 0);
*(rgb + 1) = *(bmp + 1);
*(rgb + 2) = *(bmp + 2);
rgb += 3;
bmp += 4;
}
return 0;
}
else
{//根据掩码确定RGB比特位
int Gkey, Bkey, Rkey;
if (mas->rgbGreen == 0)
Gkey = 0;
else if (mas->rgbGreen == 0xFF000000)
Gkey = 3;
else if (mas->rgbGreen == 0xFF0000)
Gkey = 2;
else if (mas->rgbGreen == 0xFF00)
Gkey = 1;
else
return 1;
if (mas->rgbBlue == 0)
Bkey = 0;
else if (mas->rgbBlue == 0xFF000000)
Bkey = 3;
else if (mas->rgbBlue == 0xFF0000)
Bkey = 2;
else if (mas->rgbBlue == 0xFF00)
Bkey = 1;
else
return 1;
if (mas->rgbRed == 0)
Rkey = 0;
else if (mas->rgbRed == 0xFF000000)
Rkey = 3;
else if (mas->rgbRed == 0xFF0000)
Rkey = 2;
else if (mas->rgbRed == 0xFF00)
Rkey = 1;
else
return 1;
for (i = 0; i < size; i++)
{
*(rgb + 0) = *(bmp + Bkey);
*(rgb + 1) = *(bmp + Gkey);
*(rgb + 2) = *(bmp + Rkey);
rgb += 3;
bmp += 4;
}
return 0;
}
}
int BMP2RGB24bit(int bitcount, int x_dim, int y_dim, void* bmpbuf, void* rgbbuf)
{
long i;
unsigned char* rgb;// raw;
unsigned char* bmp;
long size = x_dim * y_dim;
rgb = (unsigned char*)rgbbuf;
bmp = (unsigned char*)bmpbuf;
for (i = 0; i < size * 3; i++)
{
*(rgb + i) = *(bmp + i);
}
return 0;
}
int BMP2RGB16bit(int bitcount, int x_dim, int y_dim, void* bmpbuf, void* rgbbuf, void* mask)
{
long loop;
unsigned char* Data, * rgbDataOut;
long size = x_dim * y_dim * bitcount / 8;
Data = (unsigned char*)bmpbuf;
rgbDataOut = (unsigned char*)rgbbuf;
Mas16* mas;
mas = (Mas16*)mask;
if (mask == NULL