C++ RGB与YUV格式相互转换代码 笔记

6 阅读14分钟

C++ RGB与YUV格式相互转换代码

以下是完整的RGB和YUV相互转换代码实现,包含多种格式支持:

一、核心转换公式

1. BT.601标准(标清电视)

// RGB到YUV(8位范围,0-255)
Y =  0.299R + 0.587G + 0.114B
U = -0.169R - 0.331G + 0.500B + 128
V =  0.500R - 0.419G - 0.081B + 128

// YUV到RGB
R = Y + 1.402(V - 128)
G = Y - 0.344(U - 128) - 0.714(V - 128)
B = Y + 1.772(U - 128)

2. BT.709标准(高清电视)

// RGB到YUV
Y =  0.2126R + 0.7152G + 0.0722B
U = -0.0999R - 0.3360G + 0.4360B + 128
V =  0.6150R - 0.5586G - 0.0564B + 128

// YUV到RGB
R = Y + 1.5748(V - 128)
G = Y - 0.1873(U - 128) - 0.4681(V - 128)
B = Y + 1.8556(U - 128)

二、完整的C++实现

头文件:ColorConvert.h

#ifndef COLOR_CONVERT_H
#define COLOR_CONVERT_H

#include <cstdint>
#include <vector>
#include <cmath>

// 颜色转换标准
enum ColorStandard {
    STANDARD_BT601,  // ITU-R BT.601 (标清)
    STANDARD_BT709,  // ITU-R BT.709 (高清)
    STANDARD_BT2020  // ITU-R BT.2020 (超高清)
};

// RGB像素结构
struct RGBPixel {
    uint8_t r, g, b;
    
    RGBPixel() : r(0), g(0), b(0) {}
    RGBPixel(uint8_t red, uint8_t green, uint8_t blue) 
        : r(red), g(green), b(blue) {}
};

// YUV像素结构
struct YUVPixel {
    uint8_t y, u, v;
    
    YUVPixel() : y(0), u(0), v(0) {}
    YUVPixel(uint8_t y_val, uint8_t u_val, uint8_t v_val) 
        : y(y_val), u(u_val), v(v_val) {}
};

// YUV420平面格式
struct YUV420Image {
    int width, height;
    std::vector<uint8_t> y_plane;    // Y分量,大小 = width * height
    std::vector<uint8_t> u_plane;    // U分量,大小 = (width/2) * (height/2)
    std::vector<uint8_t> v_plane;    // V分量,大小 = (width/2) * (height/2)
    
    YUV420Image(int w, int h) : width(w), height(h) {
        y_plane.resize(width * height);
        u_plane.resize((width/2) * (height/2));
        v_plane.resize((width/2) * (height/2));
    }
};

// YUV422交错格式
struct YUV422Image {
    int width, height;
    std::vector<uint8_t> data;  // YUYV或UYVY格式
    
    YUV422Image(int w, int h) : width(w), height(h) {
        data.resize(width * height * 2);  // 每个像素2字节
    }
};

// RGB图像
struct RGBImage {
    int width, height;
    std::vector<RGBPixel> pixels;
    
    RGBImage(int w, int h) : width(w), height(h) {
        pixels.resize(width * height);
    }
    
    RGBPixel& at(int x, int y) {
        return pixels[y * width + x];
    }
    
    const RGBPixel& at(int x, int y) const {
        return pixels[y * width + x];
    }
};

// 颜色转换类
class ColorConverter {
public:
    // 设置转换标准
    static void setStandard(ColorStandard standard);
    
    // 像素级转换
    static YUVPixel RGBtoYUV(const RGBPixel& rgb);
    static RGBPixel YUVtoRGB(const YUVPixel& yuv);
    
    // 整张图像转换
    static YUV420Image RGBtoYUV420(const RGBImage& rgb);
    static RGBImage YUV420toRGB(const YUV420Image& yuv);
    
    static YUV422Image RGBtoYUV422(const RGBImage& rgb);
    static RGBImage YUV422toRGB(const YUV422Image& yuv);
    
    // NV12/NV21格式转换(手机常用)
    static void RGBtoNV12(const RGBImage& rgb, 
                         std::vector<uint8_t>& y_plane,
                         std::vector<uint8_t>& uv_plane);
    static RGBImage NV12toRGB(int width, int height,
                             const uint8_t* y_plane,
                             const uint8_t* uv_plane);
    
    // 性能优化的SIMD版本(如果可用)
#ifdef __SSE2__
    static void RGBtoYUV_SSE(const uint8_t* rgb, uint8_t* yuv, int size);
    static void YUVtoRGB_SSE(const uint8_t* yuv, uint8_t* rgb, int size);
#endif

private:
    static ColorStandard current_standard;
    
    // 辅助函数
    static uint8_t clip(int value, int min = 0, int max = 255);
    
    // 根据标准获取系数
    static void getCoefficients(ColorStandard std, 
                               float& kr, float& kg, float& kb);
};

#endif // COLOR_CONVERT_H

实现文件:ColorConvert.cpp

#include "ColorConvert.h"
#include <algorithm>
#include <cstring>

ColorStandard ColorConverter::current_standard = STANDARD_BT601;

// 设置转换标准
void ColorConverter::setStandard(ColorStandard standard) {
    current_standard = standard;
}

// 获取系数
void ColorConverter::getCoefficients(ColorStandard std, 
                                   float& kr, float& kg, float& kb) {
    switch (std) {
        case STANDARD_BT601:
            kr = 0.299f;
            kg = 0.587f;
            kb = 0.114f;
            break;
        case STANDARD_BT709:
            kr = 0.2126f;
            kg = 0.7152f;
            kb = 0.0722f;
            break;
        case STANDARD_BT2020:
            kr = 0.2627f;
            kg = 0.6780f;
            kb = 0.0593f;
            break;
        default:
            kr = 0.299f;
            kg = 0.587f;
            kb = 0.114f;
    }
}

// 限制值在[min, max]范围内
uint8_t ColorConverter::clip(int value, int min, int max) {
    if (value < min) return min;
    if (value > max) return max;
    return static_cast<uint8_t>(value);
}

// 单个像素RGB转YUV
YUVPixel ColorConverter::RGBtoYUV(const RGBPixel& rgb) {
    float kr, kg, kb;
    getCoefficients(current_standard, kr, kg, kb);
    
    float r = rgb.r;
    float g = rgb.g;
    float b = rgb.b;
    
    // 计算Y分量
    float y = kr * r + kg * g + kb * b;
    
    // 计算U分量(Cb)
    float u = 0.5f * (b - y) / (1.0f - kb) + 128.0f;
    
    // 计算V分量(Cr)
    float v = 0.5f * (r - y) / (1.0f - kr) + 128.0f;
    
    // 简化公式(更快)
    // int y = (299 * r + 587 * g + 114 * b) / 1000;
    // int u = ((-169 * r - 331 * g + 500 * b) / 1000) + 128;
    // int v = ((500 * r - 419 * g - 81 * b) / 1000) + 128;
    
    return YUVPixel(clip(static_cast<int>(y)),
                   clip(static_cast<int>(u)),
                   clip(static_cast<int>(v)));
}

// 单个像素YUV转RGB
RGBPixel ColorConverter::YUVtoRGB(const YUVPixel& yuv) {
    float kr, kg, kb;
    getCoefficients(current_standard, kr, kg, kb);
    
    float y = yuv.y;
    float u = yuv.u - 128.0f;
    float v = yuv.v - 128.0f;
    
    // 计算RGB
    float r = y + v * (1.0f - kr);
    float g = y - u * kb * (1.0f - kb) / (kg) - v * kr * (1.0f - kr) / (kg);
    float b = y + u * (1.0f - kb);
    
    // 简化公式(更快)
    // int r = y + 1.402 * (v - 128);
    // int g = y - 0.344 * (u - 128) - 0.714 * (v - 128);
    // int b = y + 1.772 * (u - 128);
    
    return RGBPixel(clip(static_cast<int>(r)),
                   clip(static_cast<int>(g)),
                   clip(static_cast<int>(b)));
}

// RGB图像转YUV420平面格式
YUV420Image ColorConverter::RGBtoYUV420(const RGBImage& rgb) {
    int width = rgb.width;
    int height = rgb.height;
    
    YUV420Image yuv(width, height);
    
    // 遍历每个像素
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            const RGBPixel& rgb_pixel = rgb.at(x, y);
            YUVPixel yuv_pixel = RGBtoYUV(rgb_pixel);
            
            // 存储Y分量
            yuv.y_plane[y * width + x] = yuv_pixel.y;
            
            // 每2x2块取一个UV分量
            if ((x % 2 == 0) && (y % 2 == 0)) {
                int uv_index = (y/2) * (width/2) + (x/2);
                
                // 计算2x2块的UV平均值
                int u_sum = 0, v_sum = 0;
                int count = 0;
                
                for (int dy = 0; dy < 2 && (y + dy) < height; ++dy) {
                    for (int dx = 0; dx < 2 && (x + dx) < width; ++dx) {
                        const RGBPixel& neighbor = rgb.at(x + dx, y + dy);
                        YUVPixel neighbor_yuv = RGBtoYUV(neighbor);
                        u_sum += neighbor_yuv.u;
                        v_sum += neighbor_yuv.v;
                        count++;
                    }
                }
                
                if (count > 0) {
                    yuv.u_plane[uv_index] = static_cast<uint8_t>(u_sum / count);
                    yuv.v_plane[uv_index] = static_cast<uint8_t>(v_sum / count);
                }
            }
        }
    }
    
    return yuv;
}

// YUV420转RGB图像
RGBImage ColorConverter::YUV420toRGB(const YUV420Image& yuv) {
    int width = yuv.width;
    int height = yuv.height;
    
    RGBImage rgb(width, height);
    
    // 遍历每个像素
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            // 获取Y分量
            uint8_t y_val = yuv.y_plane[y * width + x];
            
            // 计算对应的UV索引
            int uv_x = x / 2;
            int uv_y = y / 2;
            int uv_index = uv_y * (width/2) + uv_x;
            
            // 获取UV分量
            uint8_t u_val = yuv.u_plane[uv_index];
            uint8_t v_val = yuv.v_plane[uv_index];
            
            // 转换到RGB
            YUVPixel yuv_pixel(y_val, u_val, v_val);
            rgb.at(x, y) = YUVtoRGB(yuv_pixel);
        }
    }
    
    return rgb;
}

// RGB转YUV422(YUYV格式)
YUV422Image ColorConverter::RGBtoYUV422(const RGBImage& rgb) {
    int width = rgb.width;
    int height = rgb.height;
    
    YUV422Image yuv422(width, height);
    
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; x += 2) {  // 每次处理两个像素
            // 获取两个RGB像素
            const RGBPixel& rgb1 = rgb.at(x, y);
            const RGBPixel& rgb2 = rgb.at(x + 1, y);
            
            // 转换为YUV
            YUVPixel yuv1 = RGBtoYUV(rgb1);
            YUVPixel yuv2 = RGBtoYUV(rgb2);
            
            // YUYV格式:Y0 U0 Y1 V0
            int base_index = (y * width + x) * 2;
            yuv422.data[base_index]     = yuv1.y;  // Y0
            yuv422.data[base_index + 1] = (yuv1.u + yuv2.u) / 2;  // U0
            yuv422.data[base_index + 2] = yuv2.y;  // Y1
            yuv422.data[base_index + 3] = (yuv1.v + yuv2.v) / 2;  // V0
        }
    }
    
    return yuv422;
}

// YUV422(YUYV格式)转RGB
RGBImage ColorConverter::YUV422toRGB(const YUV422Image& yuv422) {
    int width = yuv422.width;
    int height = yuv422.height;
    
    RGBImage rgb(width, height);
    
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; x += 2) {
            // YUYV格式:Y0 U0 Y1 V0
            int base_index = (y * width + x) * 2;
            
            uint8_t y0 = yuv422.data[base_index];
            uint8_t u0 = yuv422.data[base_index + 1];
            uint8_t y1 = yuv422.data[base_index + 2];
            uint8_t v0 = yuv422.data[base_index + 3];
            
            // 第一个像素
            YUVPixel yuv1(y0, u0, v0);
            rgb.at(x, y) = YUVtoRGB(yuv1);
            
            // 第二个像素
            YUVPixel yuv2(y1, u0, v0);
            rgb.at(x + 1, y) = YUVtoRGB(yuv2);
        }
    }
    
    return rgb;
}

// RGB转NV12格式(YUV420 semi-planar)
void ColorConverter::RGBtoNV12(const RGBImage& rgb,
                              std::vector<uint8_t>& y_plane,
                              std::vector<uint8_t>& uv_plane) {
    int width = rgb.width;
    int height = rgb.height;
    
    y_plane.resize(width * height);
    uv_plane.resize(width * height / 2);  // UV交错存储
    
    // 先计算所有Y分量
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            YUVPixel yuv = RGBtoYUV(rgb.at(x, y));
            y_plane[y * width + x] = yuv.y;
        }
    }
    
    // 计算UV分量(2x2下采样)
    int uv_index = 0;
    for (int y = 0; y < height; y += 2) {
        for (int x = 0; x < width; x += 2) {
            int u_sum = 0, v_sum = 0;
            int count = 0;
            
            // 计算2x2块的UV平均值
            for (int dy = 0; dy < 2 && (y + dy) < height; ++dy) {
                for (int dx = 0; dx < 2 && (x + dx) < width; ++dx) {
                    YUVPixel yuv = RGBtoYUV(rgb.at(x + dx, y + dy));
                    u_sum += yuv.u;
                    v_sum += yuv.v;
                    count++;
                }
            }
            
            if (count > 0) {
                uv_plane[uv_index++] = static_cast<uint8_t>(u_sum / count);  // U
                uv_plane[uv_index++] = static_cast<uint8_t>(v_sum / count);  // V
            }
        }
    }
}

// NV12转RGB
RGBImage ColorConverter::NV12toRGB(int width, int height,
                                  const uint8_t* y_plane,
                                  const uint8_t* uv_plane) {
    RGBImage rgb(width, height);
    
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            // 获取Y分量
            uint8_t y_val = y_plane[y * width + x];
            
            // 计算UV索引
            int uv_x = x / 2;
            int uv_y = y / 2;
            int uv_index = (uv_y * (width/2) + uv_x) * 2;
            
            // 获取UV分量
            uint8_t u_val = uv_plane[uv_index];
            uint8_t v_val = uv_plane[uv_index + 1];
            
            // 转换到RGB
            YUVPixel yuv_pixel(y_val, u_val, v_val);
            rgb.at(x, y) = YUVtoRGB(yuv_pixel);
        }
    }
    
    return rgb;
}

// 优化的整数运算版本(无浮点数)
namespace FastConvert {
    
    // 快速RGB转YUV(整数运算)
    inline void RGB24_to_YUV444(uint8_t r, uint8_t g, uint8_t b,
                               uint8_t& y, uint8_t& u, uint8_t& v) {
        // 使用整数运算和查表优化
        static const int y_coeff[3] = {4899, 9617, 1868};  // 0.299*16384, 0.587*16384, 0.114*16384
        static const int u_coeff[3] = {-2769, -5427, 8192}; // -0.169*16384, -0.331*16384, 0.5*16384
        static const int v_coeff[3] = {8192, -6866, -1327}; // 0.5*16384, -0.419*16384, -0.081*16384
        
        int y_val = (y_coeff[0] * r + y_coeff[1] * g + y_coeff[2] * b) >> 14;
        int u_val = (u_coeff[0] * r + u_coeff[1] * g + u_coeff[2] * b + (128 << 14)) >> 14;
        int v_val = (v_coeff[0] * r + v_coeff[1] * g + v_coeff[2] * b + (128 << 14)) >> 14;
        
        y = ColorConverter::clip(y_val);
        u = ColorConverter::clip(u_val);
        v = ColorConverter::clip(v_val);
    }
    
    // 快速YUV转RGB(整数运算)
    inline void YUV444_to_RGB24(uint8_t y, uint8_t u, uint8_t v,
                               uint8_t& r, uint8_t& g, uint8_t& b) {
        int c = y - 16;
        int d = u - 128;
        int e = v - 128;
        
        // 使用整数运算
        int r_val = (298 * c + 409 * e + 128) >> 8;
        int g_val = (298 * c - 100 * d - 208 * e + 128) >> 8;
        int b_val = (298 * c + 516 * d + 128) >> 8;
        
        r = ColorConverter::clip(r_val);
        g = ColorConverter::clip(g_val);
        b = ColorConverter::clip(b_val);
    }
    
    // 批量转换RGB到YUV420
    void RGB24_to_YUV420(const uint8_t* rgb, int width, int height,
                        uint8_t* y_plane, uint8_t* u_plane, uint8_t* v_plane) {
        int y_index = 0;
        int uv_index = 0;
        
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                int rgb_index = (y * width + x) * 3;
                uint8_t r = rgb[rgb_index];
                uint8_t g = rgb[rgb_index + 1];
                uint8_t b = rgb[rgb_index + 2];
                
                uint8_t y_val, u_val, v_val;
                RGB24_to_YUV444(r, g, b, y_val, u_val, v_val);
                
                y_plane[y_index++] = y_val;
                
                // 每2x2块取一个UV
                if ((x % 2 == 0) && (y % 2 == 0)) {
                    u_plane[uv_index] = u_val;
                    v_plane[uv_index] = v_val;
                    uv_index++;
                }
            }
        }
    }
}

测试程序:main.cpp

#include "ColorConvert.h"
#include <iostream>
#include <chrono>
#include <fstream>
#include <cassert>

// 生成测试图像
RGBImage createTestPattern(int width, int height) {
    RGBImage image(width, height);
    
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            // 创建渐变图案
            uint8_t r = static_cast<uint8_t>(255 * x / width);
            uint8_t g = static_cast<uint8_t>(255 * y / height);
            uint8_t b = static_cast<uint8_t>(128 + 127 * sin(x * 0.1) * cos(y * 0.1));
            
            image.at(x, y) = RGBPixel(r, g, b);
        }
    }
    
    return image;
}

// 保存RGB图像为PPM文件(用于验证)
void savePPM(const std::string& filename, const RGBImage& image) {
    std::ofstream file(filename, std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "无法打开文件: " << filename << std::endl;
        return;
    }
    
    // PPM头
    file << "P6\n";
    file << image.width << " " << image.height << "\n";
    file << "255\n";
    
    // RGB数据
    for (const auto& pixel : image.pixels) {
        file << pixel.r << pixel.g << pixel.b;
    }
    
    file.close();
    std::cout << "保存图像到: " << filename << std::endl;
}

// 保存YUV420为YUV文件
void saveYUV420(const std::string& filename, const YUV420Image& yuv) {
    std::ofstream file(filename, std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "无法打开文件: " << filename << std::endl;
        return;
    }
    
    // 写入Y分量
    file.write(reinterpret_cast<const char*>(yuv.y_plane.data()), 
               yuv.y_plane.size());
    
    // 写入U分量
    file.write(reinterpret_cast<const char*>(yuv.u_plane.data()), 
               yuv.u_plane.size());
    
    // 写入V分量
    file.write(reinterpret_cast<const char*>(yuv.v_plane.data()), 
               yuv.v_plane.size());
    
    file.close();
    std::cout << "保存YUV420到: " << filename << std::endl;
}

// 测试性能
void benchmark(const RGBImage& rgb) {
    std::cout << "\n=== 性能测试 ===" << std::endl;
    std::cout << "图像大小: " << rgb.width << "x" << rgb.height 
              << " (" << rgb.pixels.size() << " 像素)" << std::endl;
    
    // 测试RGB转YUV420
    auto start = std::chrono::high_resolution_clock::now();
    auto yuv = ColorConverter::RGBtoYUV420(rgb);
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "RGB转YUV420耗时: " << duration.count() << " ms" << std::endl;
    
    // 测试YUV420转RGB
    start = std::chrono::high_resolution_clock::now();
    auto rgb_back = ColorConverter::YUV420toRGB(yuv);
    end = std::chrono::high_resolution_clock::now();
    duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "YUV420转RGB耗时: " << duration.count() << " ms" << std::endl;
    
    // 验证转换正确性
    int error_count = 0;
    for (size_t i = 0; i < rgb.pixels.size(); ++i) {
        const RGBPixel& orig = rgb.pixels[i];
        const RGBPixel& converted = rgb_back.pixels[i];
        
        // 允许少量误差(由于YUV的色度下采样)
        if (abs(orig.r - converted.r) > 2 || 
            abs(orig.g - converted.g) > 2 || 
            abs(orig.b - converted.b) > 2) {
            error_count++;
        }
    }
    
    std::cout << "转换误差像素数: " << error_count 
              << " (" << (error_count * 100.0 / rgb.pixels.size()) << "%)" << std::endl;
}

// 测试不同格式
void testAllFormats() {
    std::cout << "\n=== 测试所有格式 ===" << std::endl;
    
    // 创建测试图像
    RGBImage test_image = createTestPattern(640, 480);
    
    // 1. 测试RGB↔YUV420
    std::cout << "1. 测试RGB↔YUV420转换..." << std::endl;
    YUV420Image yuv420 = ColorConverter::RGBtoYUV420(test_image);
    RGBImage rgb_back = ColorConverter::YUV420toRGB(yuv420);
    
    // 保存结果
    savePPM("test_original.ppm", test_image);
    savePPM("test_recovered.ppm", rgb_back);
    saveYUV420("test_yuv420.yuv", yuv420);
    
    // 2. 测试RGB↔YUV422
    std::cout << "2. 测试RGB↔YUV422转换..." << std::endl;
    YUV422Image yuv422 = ColorConverter::RGBtoYUV422(test_image);
    RGBImage rgb_back2 = ColorConverter::YUV422toRGB(yuv422);
    savePPM("test_yuv422_recovered.ppm", rgb_back2);
    
    // 3. 测试NV12格式
    std::cout << "3. 测试NV12格式转换..." << std::endl;
    std::vector<uint8_t> y_plane, uv_plane;
    ColorConverter::RGBtoNV12(test_image, y_plane, uv_plane);
    
    RGBImage rgb_nv12 = ColorConverter::NV12toRGB(
        test_image.width, test_image.height,
        y_plane.data(), uv_plane.data()
    );
    savePPM("test_nv12_recovered.ppm", rgb_nv12);
    
    // 4. 测试不同标准
    std::cout << "4. 测试不同标准..." << std::endl;
    
    ColorConverter::setStandard(STANDARD_BT601);
    YUVPixel yuv601 = ColorConverter::RGBtoYUV(RGBPixel(255, 0, 0));
    std::cout << "BT.601 红色: Y=" << (int)yuv601.y 
              << " U=" << (int)yuv601.u 
              << " V=" << (int)yuv601.v << std::endl;
    
    ColorConverter::setStandard(STANDARD_BT709);
    YUVPixel yuv709 = ColorConverter::RGBtoYUV(RGBPixel(255, 0, 0));
    std::cout << "BT.709 红色: Y=" << (int)yuv709.y 
              << " U=" << (int)yuv709.u 
              << " V=" << (int)yuv709.v << std::endl;
}

int main() {
    std::cout << "=== RGB/YUV转换测试 ===" << std::endl;
    
    try {
        // 创建测试图像
        RGBImage test_image = createTestPattern(800, 600);
        
        // 测试所有格式
        testAllFormats();
        
        // 性能测试
        benchmark(test_image);
        
        // 测试快速转换
        std::cout << "\n=== 测试快速转换 ===" << std::endl;
        RGBPixel rgb_pixel(100, 150, 200);
        uint8_t r, g, b, y, u, v;
        
        // 使用快速版本
        FastConvert::RGB24_to_YUV444(rgb_pixel.r, rgb_pixel.g, rgb_pixel.b, y, u, v);
        FastConvert::YUV444_to_RGB24(y, u, v, r, g, b);
        
        std::cout << "原始RGB: (" << (int)rgb_pixel.r << ", " 
                  << (int)rgb_pixel.g << ", " << (int)rgb_pixel.b << ")" << std::endl;
        std::cout << "转换后RGB: (" << (int)r << ", " << (int)g << ", " << (int)b << ")" << std::endl;
        
        std::cout << "\n测试完成!" << std::endl;
        
    } catch (const std::exception& e) {
        std::cerr << "错误: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

CMake构建文件:CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(ColorConverter)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 添加可执行文件
add_executable(ColorConverter
    ColorConvert.cpp
    main.cpp
)

# 如果支持SSE,添加编译选项
if(MSVC)
    target_compile_options(ColorConverter PRIVATE /arch:SSE2)
else()
    target_compile_options(ColorConverter PRIVATE -msse2)
endif()

# 优化选项
target_compile_options(ColorConverter PRIVATE -O2)

三、使用示例

基本使用

#include "ColorConvert.h"

int main() {
    // 设置转换标准
    ColorConverter::setStandard(STANDARD_BT709);
    
    // 单像素转换
    RGBPixel rgb(255, 0, 0);  // 红色
    YUVPixel yuv = ColorConverter::RGBtoYUV(rgb);
    
    // 整图转换
    RGBImage rgb_image(1920, 1080);
    // ... 填充rgb_image数据 ...
    
    // 转换为YUV420
    YUV420Image yuv420 = ColorConverter::RGBtoYUV420(rgb_image);
    
    // 转换回RGB
    RGBImage rgb_back = ColorConverter::YUV420toRGB(yuv420);
    
    return 0;
}

视频帧处理示例

#include "ColorConvert.h"
#include <opencv2/opencv.hpp>  // 可选:与OpenCV集成

class VideoProcessor {
public:
    void processFrame(const cv::Mat& bgr_frame) {
        // OpenCV使用BGR顺序
        int width = bgr_frame.cols;
        int height = bgr_frame.rows;
        
        // 将BGR转换为RGB
        RGBImage rgb_image(width, height);
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                cv::Vec3b bgr = bgr_frame.at<cv::Vec3b>(y, x);
                rgb_image.at(x, y) = RGBPixel(bgr[2], bgr[1], bgr[0]);
            }
        }
        
        // 转换为YUV420(视频编码常用格式)
        YUV420Image yuv420 = ColorConverter::RGBtoYUV420(rgb_image);
        
        // 进行视频处理...
        processYUVFrame(yuv420);
        
        // 转换回BGR显示
        RGBImage rgb_processed = ColorConverter::YUV420toRGB(yuv420);
        cv::Mat processed_frame(height, width, CV_8UC3);
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                const RGBPixel& rgb = rgb_processed.at(x, y);
                processed_frame.at<cv::Vec3b>(y, x) = 
                    cv::Vec3b(rgb.b, rgb.g, rgb.r);  // 转回BGR
            }
        }
        
        // 显示结果
        cv::imshow("Processed", processed_frame);
    }
    
private:
    void processYUVFrame(const YUV420Image& yuv) {
        // 这里可以添加YUV域的处理,如:
        // - 亮度调整(修改Y分量)
        // - 饱和度调整(修改UV分量)
        // - 色度键控
        // - 等等
    }
};

四、性能优化建议

  1. 使用整数运算:避免浮点数运算,使用定点数或查表
  2. SIMD指令集:使用SSE/AVX进行并行计算
  3. 多线程:将图像分块并行处理
  4. 缓存友好:按内存顺序访问数据
  5. 查表法:预计算转换表(适用于8位数据)

查表法优化示例

class FastColorConverter {
private:
    static uint8_t rgb_to_y_table[256][256][256];  // 太大,不实际
    static uint8_t rgb_to_u_table[256][256][256];
    static uint8_t rgb_to_v_table[256][256][256];
    
    // 更实际的做法:分离查表
    static int16_t r_to_y[256];
    static int16_t g_to_y[256];
    static int16_t b_to_y[256];
    // ... 其他分量
    
public:
    static void initTables() {
        for (int i = 0; i < 256; ++i) {
            r_to_y[i] = static_cast<int16_t>(0.299 * i * 256);
            g_to_y[i] = static_cast<int16_t>(0.587 * i * 256);
            b_to_y[i] = static_cast<int16_t>(0.114 * i * 256);
            // ... 初始化其他表
        }
    }
    
    static uint8_t RGBtoY(uint8_t r, uint8_t g, uint8_t b) {
        int y = (r_to_y[r] + g_to_y[g] + b_to_y[b]) >> 8;
        return static_cast<uint8_t>(y > 255 ? 255 : (y < 0 ? 0 : y));
    }
};

五、注意事项

  1. 颜色范围:YUV通常使用有限范围(16-235 for Y,16-240 for UV)
  2. 色度下采样:YUV420/422会损失色度信息
  3. 伽马校正:RGB数据可能是非线性(sRGB)
  4. 内存对齐:视频处理需要注意内存对齐以提高性能
  5. 平台差异:不同的硬件/API可能有不同的YUV格式排列

这个实现提供了完整的RGB/YUV转换功能,支持多种格式,可以直接用于视频处理、图像处理等应用。