C++实现YUV转RGBA

68 篇文章 2 订阅
3 篇文章 0 订阅

本文受了https://blog.csdn.net/fanyun_01/article/details/100068351 的启发。但是该文章有一处谬误:YUV420的内存排列不是按照下图所示的。

正确的排列应如下(来源维基百科yuv条目):

根据正确的内存排列,写出相应的代码:

YUV2RGB.h

#pragma once
#include <qglobal.h>

void vFillY_U_V_Buff(const quint32 & ui32Width, const quint32 & ui32Height, unsigned char * pSrcData,
                     unsigned char * pUData, unsigned char * pVData);

void vFillBGRA(unsigned char * pBGRA, const quint32 & ui32Width, const quint32 & ui32Height, unsigned char * pSrcData,
               unsigned char * pUData, unsigned char * pVData);
void vInitLookupTable(void);

YUV2RGB.cpp

#include "YUV2RGB.h"

namespace {
    float   fRGBYUV1402[256];
    float   fRGBYUV0344[256];
    float   fRGBYUV0714[256];
    float   fRGBYUV1772[256];
}


void vInitLookupTable(void)
{
    for(int k = 0; k < 256; k++)
    {
        fRGBYUV1402[k] = (float)(1.402 * (k - 128));
        fRGBYUV0344[k] = (float)(0.34414 * (k - 128));
        fRGBYUV0714[k] = (float)(0.71414 * (k - 128));
        fRGBYUV1772[k] = (float)(1.772 * (k - 128));
    }
}

void vFillY_U_V_Buff(const quint32 & ui32Width, const quint32 & ui32Height, unsigned char * pSrcData,
                     unsigned char * pUData, unsigned char * pVData)
{
    //YBuff不需要填,因为pSrcData的首字节就是Y分量的第一字节
    unsigned char * pDstU1, * pDstU2, * pDstV1, * pDstV2, * pu, * pv;
    unsigned char * pUV = pSrcData + ui32Width * ui32Height;
    quint32 ui32HalfHeight = ui32Height/2, ui32HalfWidth = ui32Width / 2;
    for(quint32 m = 0; m < ui32HalfHeight; m++)
    {
        pDstU1 = pUData + 2 * m * ui32Width;
        pDstU2 = pUData + (2 * m + 1) * ui32Width;
        pDstV1 = pVData + 2 * m * ui32Width;
        pDstV2 = pVData + (2 * m + 1) * ui32Width;

        pu = pUV + m * ui32HalfWidth;
        pv = pUV + ui32HalfHeight * ui32HalfWidth + m * ui32HalfWidth;

        for(quint32 n = 0; n < ui32HalfWidth; n++)
        {
            *pDstU1 = *pu; *pDstU2 = *pu;
            *pDstV1 = *pv; *pDstV2 = *pv;
            pDstU1++; pDstU2++;
            pDstV1++; pDstV2++;
            *pDstU1 = *pu; *pDstU2 = *pu;
            *pDstV1 = *pv; *pDstV2 = *pv;
            pDstU1++; pDstU2++;
            pDstV1++; pDstV2++;
            pu++; pv++;
        }
    }
}

void vFillBGRA(unsigned char * pBGRA, const quint32 & ui32Width, const quint32 & ui32Height, unsigned char * pYData,
               unsigned char * pUData, unsigned char * pVData)
{
    unsigned char * pB = pBGRA, *pG, *pR, *pA;
    float fR, fG, fB;
    for(quint32 m = 0; m < ui32Height; m++)
    {
        for(quint32 n = 0; n < ui32Width; n++)
        {
            pG = pB + 1;
            pR = pB + 2;
            pA = pB + 3;
            fR = *pYData + fRGBYUV1402[*pVData];
            fG = *pYData - fRGBYUV0344[*pUData] - fRGBYUV0714[*pVData];
            fB = *pYData + fRGBYUV1772[*pUData];

            *pR = fR > 255 ? 255 : (fR < 0 ? 0 : fR);
            *pG = fG > 255 ? 255 : (fG < 0 ? 0 : fG);
            *pB = fB > 255 ? 255 : (fB < 0 ? 0 : fB);
            *pA = 255;
            pYData++;
            pUData++;
            pVData++;
            pB += 4;
        }
    }
}

具体原理不再赘述。这里只介绍使用以上代码进行YUV->RGBA的转化方法。

第一步,调用vInitLookupTable()计算转化系数:

namespace {
    float   fRGBYUV1402[256];
    float   fRGBYUV0344[256];
    float   fRGBYUV0714[256];
    float   fRGBYUV1772[256];
}

转化的第二步,是把压缩过的U、V数据还原成没压缩的格式。调用vFillY_U_V_Buff()。其第一个输入参数是图片的宽度,第二个参数是图片高度,第三个参数是YUV数据的首地址。由于YUV数据的开头是Y数据,而且没有被压缩,所以也可以看成是Y分量的首地址。第四个参数是U分量的首地址。这块地址占用(宽度*高度)个像素,在调用vFillY_U_V_Buff之前就应该被分配。vFillY_U_V_Buff执行完毕后,这块内存就被填入相应的U分量数据。第五个参数是V分量的首地址。这块地址占用(宽度*高度)个像素,在调用vFillY_U_V_Buff之前就应该被分配。vFillY_U_V_Buff执行完毕后,这块内存就被填入相应的V分量数据。

第三步,将YUV数据转化为RGBA。qt的RGBA格式,以B为第一个分量,后面依次为GRA。所以vFillBGRA()函数填充的每一个像素,其次序都是BGRA。vFillBGRA()的第一个输入参数是一个地址,就是用来存储BGRA数据的,在调用vFillBGRA之前,应该预先给这个地址开辟一块内存,大小为(4 * 宽度 * 高度)。第二个参数是图片宽度,第三个参数是图片高度。第四个参数是Y分量的首地址,可以直接采用前面vFillY_U_V_Buff的第三个参数;第五个输入参数是U分量的首地址,应采用vFillY_U_V_Buff的第四个参数;第六个输入参数是V分量的首地址,应采用vFillY_U_V_Buff的第五个地址。最终的结果保存在第一个参数pBGRA中。

 

示例:

extern    const quint32   cnst_ui32Width = 1920;
extern    const quint32   cnst_ui32Height = 1080;
unsigned char * m_pBuffU, * m_pBuffV;

{
    ......
    vInitLookupTable();
    vInitBuff();
    unsigned char * pBuffYUV = new unsigned char[cnst_ui32Width * cnst_ui32Height*3/2];
        unsigned char * pBuffBGRA = new unsigned char[cnst_ui32Width * cnst_ui32Height *    4];
    ......
    vFillY_U_V_Buff(cnst_ui32Width, cnst_ui32Height, pBuffYUV, m_pBuffU, m_pBuffV);
    vFillBGRA(pBuffBGRA, cnst_ui32Width, cnst_ui32Height, pBuffYUV, m_pBuffU, m_pBuffV);
    ......
    vReleaseBuff();
}

void vInitBuff(void)
{
    m_pBuffU = new unsigned char[cnst_ui32Width * cnst_ui32Height];
    m_pBuffV = new unsigned char[cnst_ui32Width * cnst_ui32Height];
}

void vReleaseBuff(void)
{
    delete [] m_pBuffU;
    delete [] m_pBuffV;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值