RGB转YUV

本文介绍了使用C语言和CUDA实现RGB24图像格式到YUV420(NV12/YV12)格式的转换方法。C语言实现直接在CPU上运行,而CUDA实现则利用GPU加速处理。两种实现都详细展示了像素颜色空间转换的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C实现:

void rgb24_to_nv12(uint8_t* data, int w, int h, uint8_t* yuv)
{
    uint32_t row_bytes;
    uint8_t* uv;
    uint8_t* y;
    uint8_t r, g, b;
    uint8_t y_val, u_val, v_val;
    uint32_t off;
    uint32_t off_uv;
    uint8_t* img;
    int i, j;
    y = yuv;
    uv = yuv + w * h;
    off = 0;
    row_bytes = (w * 3 + 3) & ~3;
    h = h & ~1;
    //先转换Y
    for (i = 0; i < h; i++)
    {   
        img = data + off;
        for (j = 0; j < w; j++)
        {
            b = *img++;
            g = *img++;
            r = *img++;
            y_val = (uint8_t)(((int)(30 * r) + (int)(59 * g) + (int)(11 * b)) / 100);
            *y++ = y_val;
        }
        off += row_bytes;
    }
    //转换uv
    off = 0;
    for (i = 0; i < h; i += 2)
    {
        img = data + off;
        for (j = 0; j < w; j+=2)
        {
            b = *img++;
            g = *img++;
            r = *img++;
            img += 3;
            u_val = (uint8_t)(((int)(-17 * r) - (int)(33 * g) + (int)(50 * b) + 12800) / 100);
            v_val = (uint8_t)(((int)(50 * r) - (int)(42 * g) - (int)(8 * b) + 12800) / 100);
            *uv++ = v_val;
            *uv++ = u_val;
        }
        off += row_bytes;
        off += row_bytes;
    }
}

CUDA实现:

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#define  MEMORY_ALGN_DEVICE  511

__host__ __device__ __forceinline__ int divUp(int total, int grain)
{
    return (total + grain - 1) / grain;
}

__device__ __forceinline__ void rgb_to_y(const uint8_t b, const uint8_t g, const uint8_t r, uint8_t& y)
{
    y = static_cast<uint8_t>(((int)(30 * r) + (int)(59 * g) + (int)(11 * b)) / 100);
}

__device__ __forceinline__ void rgb_to_yuv(const uint8_t b, const uint8_t g, const uint8_t r, uint8_t& y, uint8_t& u, uint8_t& v)
{
    rgb_to_y(b, g, r, y);
    u = static_cast<uint8_t>(((int)(-17 * r) - (int)(33 * g) + (int)(50 * b) + 12800) / 100);
    v = static_cast<uint8_t>(((int)(50 * r) - (int)(42 * g) - (int)(8 * b) + 12800) / 100);
}

__global__ void RGB24_to_YV12_kernel(uint8_t* data,int w, int h, uint32_t row_bytes, uint8_t* y_data, uint8_t* u_data, uint8_t* v_data, uint32_t y_pitch, uint32_t uv_pitch)
    {
        const int x = (blockIdx.x * blockDim.x + threadIdx.x) * 2;
        const int y = (blockIdx.y * blockDim.y + threadIdx.y) * 2;

        if (x + 1 >= w || y + 1 >= h)
            return;

        uint8_t r, g, b;
        uint8_t y_val, u_val, v_val;
        uint8_t* img;
        //pixel at (x, y)
        img = data + y * row_bytes + x * 3;
        b = *img++;
        g = *img++;
        r = *img++;
        rgb_to_y(b, g, r, y_val);
        *(y_data + y * y_pitch + x) = y_val;

        //pixel at (x+1, y)
        b = *img++;
        g = *img++;
        r = *img++;
        rgb_to_y(b, g, r, y_val);
        *(y_data + y * y_pitch + x + 1) = y_val;

        //pixel at (x, y+1)
        img = data + (y+1) * row_bytes + x * 3;
        b = *img++;
        g = *img++;
        r = *img++;
        rgb_to_y(b, g, r,  y_val);
        *(y_data + (y+1) * y_pitch + x) = y_val;

        //pixel at (x+1, y+1)
        b = *img++;
        g = *img++;
        r = *img++;
        rgb_to_yuv(b,g, r, y_val, u_val, v_val);
        *(y_data + (y + 1) * y_pitch + x + 1) = y_val;
        *(u_data + uv_pitch * y / 2 + x / 2) = u_val;
        *(v_data + uv_pitch * y / 2 + x / 2) = v_val;
    }


void RGB24_to_YV12(uint8_t* data, uint8_t* y, uint8_t* u, uint8_t* v, int w, int h)
{
    uint32_t row_bytes;
    uint32_t y_pitch;
    uint32_t uv_pitch;
    const dim3 block(32, 8);
    const dim3 grid(divUp(w, block.x * 2), divUp(h, block.y * 2));
    row_bytes = (w * 3 + 3) & ~3;
    y_pitch = (w + MEMORY_ALGN_DEVICE) & ~MEMORY_ALGN_DEVICE;
    uv_pitch = (w / 2 + MEMORY_ALGN_DEVICE) & ~MEMORY_ALGN_DEVICE;
    RGB24_to_YV12_kernel << <grid, block >> >(data, w, h, row_bytes, y, u, v, y_pitch, uv_pitch);
    cudaGetLastError();
    cudaDeviceSynchronize();
}

CUDA实现参考opencv上的实现,转换的yuv格式是YV12.

<think>嗯,用户想知道如何将RGB颜色空间换为YUV颜色空间。首先,我需要回忆一下这两种颜色空间的基本概念。根据引用[2],RGB是基于红、绿、蓝三原色的加色系统,而YUV则是用亮度和色差来描述颜色。Y代表亮度,U和V是色度分量。 接下来,我得找出具体的换公式。用户提供的引用[1]里有YUVRGB的公式,但用户需要的是反向换,也就是RGBYUV。通常,这些换是可逆的,可以通过矩阵运算来实现。不过要注意引用[3]提到的非线性RGB,即经过伽马校正的R'G'B',可能需要调整公式。 根据常见的换方法,RGBYUV的公式可能与引用[1]中的相反。比如,亮度Y通常是R、G、B的加权和,而色度U和V则是基于颜色差异。常见的系数可能是Y = 0.299R + 0.587G + 0.114B,U = (B - Y) * 0.564,V = (R - Y) * 0.713。不过需要确认这些系数是否正确,并参考引用中的信息。 另外,引用[4]提到在编程实现时,数据类型的溢出问题,需要将unsigned char改为int类型处理中间结果,避免数值超过255或低于0。所以在代码实现时,需要注意变量类型和范围限制。 可能还需要考虑不同的YUV标准,比如YCbCr和YPbPr的区别,如引用[3]所述,不同的标准可能使用不同的系数矩阵。因此,在给出换公式时,需要明确使用的是哪一种标准,并选择合适的系数。 总结步骤应该是:1. 确定RGB是线性还是非线性,是否需要伽马校正;2. 选择合适的换矩阵;3. 实现矩阵运算,注意数据类型和溢出处理;4. 验证换结果是否正确,可能通过引用中的例子或实验来测试。 现在需要将这些整理成结构清晰的回答,包括公式、步骤、代码示例和注意事项,并生成相关问题。</think>### RGBYUV颜色空间的实现方法 #### 一、理论基础 RGBYUV换基于颜色空间的线性变换关系。根据亮度分量(Y)和色度分量(U/V)的定义,换公式可表示为矩阵运算: $$ \begin{bmatrix} Y \\ U \\ V \end{bmatrix} = \begin{bmatrix} 0.299 & 0.587 & 0.114 \\ -0.147 & -0.289 & 0.436 \\ 0.615 & -0.515 & -0.100 \end{bmatrix} \begin{bmatrix} R \\ G \\ B \end{bmatrix} + \begin{bmatrix} 0 \\ 128 \\ 128 \end{bmatrix} $$ [^2][^3] 其中: - $Y$ 取值范围 $0 \sim 255$ - $U/V$ 取值范围 $0 \sim 255$(偏移后) #### 二、实现步骤 1. **数据范围处理**:将原始RGB值从 $0 \sim 255$ 映射到 $-128 \sim 127$ 2. **矩阵乘法**:应用换矩阵计算原始YUV值 3. **偏移调整**:对U/V分量加128保证正值 4. **边界约束**:通过饱和运算确保结果在 $0 \sim 255$ 范围内 #### 三、C语言实现示例 ```c void RGB2YUV(int R, int G, int B, int* Y, int* U, int* V) { // 核心换计算 *Y = 0.299*R + 0.587*G + 0.114*B; *U = -0.147*R - 0.289*G + 0.436*B + 128; *V = 0.615*R - 0.515*G - 0.100*B + 128; // 数值饱和处理 *Y = (*Y > 255) ? 255 : (*Y < 0 ? 0 : *Y); *U = (*U > 255) ? 255 : (*U < 0 ? 0 : *U); *V = (*V > 255) ? 255 : (*V < 0 ? 0 : *V); } ``` #### 四、注意事项 1. **数据类型**:必须使用int类型处理中间结果,防止溢出(如直接使用unsigned char会导致计算结果错误)[^4] 2. **伽马校正**:若原始RGB是经过伽马校正的非线性值(R'G'B'),需先进行逆伽马校正[^3] 3. **标准差异**:不同标准(YCbCr/YPbPr)系数略有不同,需确认具体应用场景 #### 五、验证方法 1. **极端值测试**:例如纯红色(255,0,0)应满足: $$ Y = 0.299×255 ≈ 76 $$ $$ V = 0.615×255 + 128 ≈ 255 $$ 2. **反向验证**:将YUV换回RGB后与原值对比误差
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值