yuv转bmp说明2

BMP说明

BMP是BitMap的缩写,即位图,是一种非压缩格式,图像文件一般比较大,Windows系统内部各图像绘制操作都是以BMP为基础的。


BMP图像组成

BMP文件由四部分:
  1. 文件头信息,固定大小,结构体见BitmapFileHeader
  2. 图像信息头,固定大小,结构见BitmapInfoHeader
  3. 调色板,可选的,大小根据不同位数不同。
  4. 位图RGB数据,大小可以根据位数,高宽计算出来。

BMP数据结构

typedef unsigned short U16;
typedef unsigned long  U32;

typedef struct tagBitmapFileHeader
{
    U16 bfType;         /* windows下该值必需是0x4D42,即字符'BM'*/
    U32 bfSize;         /* bmp文件大小,包含bmp文件头,信息头,调色板大小,数据大小 */
    U16 bfReserved1;    /* 保留,必须设置为0 */
    U16 bfReserved2;    /* 保留,必须设置为0 */
    U32 bfOffBits;      /* rgb数据相对文件头偏移量 */
} BitmapFileHeader;

typedef struct
{
    U32 biSize;         /* 信息头大小sizeof(BitmapInfoHeader) */
    U32 biWidth;        /* 图象的宽度,以象素为单位 */
    U32 biHeight;       /* 图象的高度,以象素为单位,正值倒向位图,负值正向位图 */
    U16 biPlanes;       /* 位面数,设为1 */
    U16 biBitCount;     /* 位图位数,可以为32,24,16,8,4,1 */
    U32 biCompression;  /* 说明图象数据压缩的类型,BI_RGB(0) BI_BITFIELDS(3)等 */
    U32 biSizeImage;    /* 图像数据大小,包括位图信息大小和数据大小 */        
    U32 biXPelsPerMeter;/* 水平分辨率,一般可填0 */    
    U32 biYPelsPerMeter;/* 垂直分辨率,一般可填0*/
    U32 biClrUsed;      /* 颜色索引使用数,一般填0,表示都使用 */
    U32 biClrImportant; /* 重要颜色索引数,一般填0,表示都重要 */
} BitmapInfoHeader;

数据长度计算:
int iLineByteCnt = (((biWidth*biBitCount) + 31) >> 5) << 2;
iRGBDataSize = iLineByteCnt * biHeight;
这个公式主要做了数据行4字节对齐工作,先加31右移5位(除32)做对齐工作,左移2(乘4)转成字节。

biBitCount与biCompression
24位色与32位色biBitCount分部为24及32,biCompression固定为BI_RGB,一个像素占用3个及4个字节. 
16位色,biBitCount为16,每个像素占用2个字节,为表示2个字节RGB的位置,分为多种情况
  • biCompression为BI_RGB时,表明为RGB555格式,最高位为0,RGB各占5位,这种16位色格式无调色板信息。
  • biCompression为BI_BITFIELDS时,则表明使用掩码方式,BitmapInfoHeader后面12字节(3个int)分别为RGB的掩码,555格式下,掩码为0x7C00、0x03E0、0x001F,565格式下,它们则分别为:0xF800、0x07E0、0x001F,掩码方式挺灵活,甚至都可以自己定义16位色格式如RGB844,当然意义不大。

BMP数据结构比较简单,调试板信息是可变项,其他都是固定的,较易处理, 有几个地方需要注意
  • BitmapFileHeader结构不是4字节对齐,需注意使用#pragma pack(2)或__attribute__((aligned(2)))方式进行2直接对齐。
  • BMP图片存储时RGB数据时行一般是倒序的,填充的时候注意倒序填充。

BMP转YUV

YUV和BMP都是非压缩格式,不存在解码过程,只需要做像素点到像素点的转换,YUV转bmp公式较多,应用到不同场景,下面是ITU-R BT.601建议在数字视频领域使用的公式(带有伽马校正,图像增强效果):
R' = 1.164*(Y’-16) + 1.596*(Cr'-128)
G' = 1.164*(Y’-16) - 0.813*(Cr'-128) - 0.392*(Cb'-128)
B' = 1.164*(Y’-16) + 2.017*(Cb'-128)

效果肯定是最好的,但运算量也是最大的,如果在嵌入式设备上使用,可以使用下面的简化公式:

R = Y + 1.4075 *(V-128)
G = Y – 0.3455 *(U –128) – 0.7169 *(V –128)
B = Y + 1.779 *(U – 128)
在代码中实现该公式时,为提高计算效率,浮点运算需转为整形运算,公式可以转化为如下形式(统一先左移20,再右移20):
R = (Y<<20 + 1475871 *(V-128)) >> 20
G = (Y<<20 – 362283 *(U –128) – 751724 *(V –128)) >> 20
B = (Y <<20 + 1865417 *(U – 128)) >> 20
由于YUV每个分量都使用1个字节来存储(YUV420格式UV分量会重复使用),每个值的取值范围为(0~255),为减少乘法运算,可以把乘法部分用局部查表法实现,如下:
int V1475871[256] = {0};
int V751724[256] = {0};
int U362283[256] = {0};
int U1865417[256] = {0};

for (i = 0; i < 256; i++)
{
    V1475871[i] = (i - 128) * 1475871;
    V751724[i] = (i - 128) * 751724;
    U362283[i] = (i - 128) * 362283;
    U1865417[i] = (i - 128) * 1865417;
}

/* 使用局部查表法,公式转成如下形式: */
R = (Y<<20 + V1475871[V]) >> 20;
G = (Y<<20 – U362283[U] – V751724[V]) >> 20;
B = (Y <<20 + U1865417[U]) >> 20;
24位色及32位色一般采用局部查表法优化效率,采用完全查表法内存使用代价太大,如果16位色,则可以采用完全查表法。
局部查表法优化了乘法操作,优化效果有限(多个平台测试结果都在10%左右)。

测试程序

下面是一个yuv420转bmp的小测试程序,包含yuv420p转bmp8888,bmp888,bmp1555,bmp565这几种格式,工具和源码地址:

http://download.csdn.net/detail/t3swing/9909075


测试工程为vs2008,测试代码效率并非最优,尤其使用了函数指针,降低了10%左右的转换速度,大家可以参考并优化。



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是将YUV换为灰度BMP的C++代码示例: ```c++ #include <iostream> #include <fstream> #include <cstdlib> #include <cstring> #include <cmath> using namespace std; int main(int argc, char *argv[]) { if (argc != 4) { cerr << "Usage: " << argv[0] << " input.yuv output.bmp width*height" << endl; return 1; } int width = atoi(argv[3]); int height = atoi(strchr(argv[3], '*') + 1); ifstream fin(argv[1], ios::binary); ofstream fout(argv[2], ios::binary); if (!fin) { cerr << "Failed to open input file: " << argv[1] << endl; return 1; } if (!fout) { cerr << "Failed to open output file: " << argv[2] << endl; return 1; } int y_size = width * height; int uv_size = y_size / 4; unsigned char *yuv = new unsigned char[y_size + uv_size * 2]; unsigned char *rgb = new unsigned char[y_size * 3]; fin.read((char *)yuv, y_size + uv_size * 2); for (int i = 0; i < y_size; i++) { int y = yuv[i]; rgb[i * 3] = rgb[i * 3 + 1] = rgb[i * 3 + 2] = y; } fout << "BM"; int bmp_size = y_size * 3 + height * ((4 - (width * 3) % 4) % 4); fout.write((char *)&bmp_size, 4); fout.write("\0\0\0\0", 4); fout.write("\x36\0\0\0", 4); int header_size = 40; fout.write((char *)&header_size, 4); fout.write((char *)&width, 4); fout.write((char *)&height, 4); short planes = 1; fout.write((char *)&planes, 2); short bpp = 24; fout.write((char *)&bpp, 2); fout.write("\0\0\0\0", 4); fout.write("\0\0\0\0", 4); fout.write("\0\0\0\0", 4); fout.write("\0\0\0\0", 4); fout.write((char *)rgb, y_size * 3); delete[] yuv; delete[] rgb; return 0; } ``` 该代码将YUV数据换为灰度BMP图像。它需要3个参数:输入YUV文件名,输出BMP文件名和图像的宽度和高度(格式为“width*height”)。该代码使用C++编写,可以在Linux和Windows上编译和运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值