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分量)
// - 色度键控
// - 等等
}
};
四、性能优化建议
- 使用整数运算:避免浮点数运算,使用定点数或查表
- SIMD指令集:使用SSE/AVX进行并行计算
- 多线程:将图像分块并行处理
- 缓存友好:按内存顺序访问数据
- 查表法:预计算转换表(适用于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));
}
};
五、注意事项
- 颜色范围:YUV通常使用有限范围(16-235 for Y,16-240 for UV)
- 色度下采样:YUV420/422会损失色度信息
- 伽马校正:RGB数据可能是非线性(sRGB)
- 内存对齐:视频处理需要注意内存对齐以提高性能
- 平台差异:不同的硬件/API可能有不同的YUV格式排列
这个实现提供了完整的RGB/YUV转换功能,支持多种格式,可以直接用于视频处理、图像处理等应用。