图片格式转换:BMP转YUV(c语言实现)

2023年5月14日09:09:26

将多个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、实验整体思路

  1. 打开并读入BMP文件、定义变量,开辟存储空间。BMP文件中首先被读入的应该是文件头和信息头,读入之后才能根据其中给出的关于位图的宽高、bit数等信息进行开辟空间等操作。在本实验中,定义两个指针:BITMAPFILEHEADER File_header; BITMAPINFOHEADER Info_header; 分别用来获取文件头和信息头的数据。注意,这两种类型需要加<windows.h>头文件。
  2. 提取出RGB数据,写入缓冲区。不同bit的文件不同处理。24bit图可以直接取图片数据;16bit比较复杂,需要取像素数据转换成8bit彩色分量后再写入rgbBuf;8bit,4bit,1bit均需要构造调色板。需要注意的是图像数据需要倒置,具体代码见代码分析部分。
  3. 调用“RGB2YUV.h”函数实现转换,并写入新的YUV文件。生成视频时可以循环写入,每张图片素材30帧。打开方式“wb+”可以下次继续写入。
  4. 释放缓冲区,关闭文件。

2、BMP转为RGB

  1. 读取BMP文件,得到RGB数据:判断是否为BMP文件;判断文件是否可读出;要保证BMP图像的长必须是4的倍数,宽必须是2的倍数
  2. 通过信息头得到有效位图数据在BMP图像中的位置,进而对有效数据进行读取
  3. 注意,BMP规定:位图数据自左向右、自下向上依次存放。因此注意。Index_Data是直接从BMP文件中读取的数据倒序存放的buffer,需要变为正序的,即Data
  4. 得到的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

三、具体程序实现

  1. 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
  1. 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;
}
  1. 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
  • 作者:Lee_Larissa
  • 原文链接:https://blog.csdn.net/weixin_44221452/article/details/115250409
    更新时间:2023年5月14日09:09:26 ,共 6758 字。