3. HDR视频的色彩空间转换

HDR视频的色彩空间转换 学习卡片

任务说明

本案例实现BT.709(SDR)与BT.2020(HDR)色彩空间的主转换逻辑:将BT.709非线性RGB转换至BT.2020非线性RGB,并给出可逆的逆变换。转换过程包含RGB基色矩阵变换与传递函数(伽马/HLG)适配,确保色彩在跨空间转换时保持色相与亮度准确。

核心实现

import numpy as np

# BT.709 与 BT.2020 的 RGB 基色矩阵(线性光域)
# 矩阵 M709_to_2020: 将 BT.709 线性RGB 转换到 BT.2020 线性RGB
M709_to_2020 = np.array([
    [ 0.6274039, 0.3292830, 0.0433131],
    [ 0.0690973, 0.9195404, 0.0113623],
    [ 0.0163916, 0.0880133, 0.8955951]
])

# 逆矩阵:BT.2020 线性RGB -> BT.709 线性RGB
M2020_to_709 = np.linalg.inv(M709_to_2020)

def bt709_oetf(linear):
    """BT.709 光电转换(OETF):线性光 -> 非线性信号"""
    return np.where(linear <= 0.018,
                    4.5 * linear,
                    1.099 * np.power(linear, 0.45) - 0.099)

def bt709_eotf(nonlinear):
    """BT.709 电光转换(EOTF):非线性信号 -> 线性光"""
    return np.where(nonlinear <= 0.081,
                    nonlinear / 4.5,
                    np.power((nonlinear + 0.099) / 1.099, 1/0.45))

def bt2020_hlg_oetf(linear):
    """BT.2020 HLG OETF:线性光 -> HLG 非线性信号(归一化到[0,1])"""
    a, b, c = 0.17883277, 0.28466892, 0.55991073
    return np.where(linear <= 1/12,
                    np.sqrt(3 * linear),
                    a * np.log(12 * linear - b) + c)

def bt2020_hlg_eotf(nonlinear):
    """BT.2020 HLG EOTF:HLG 非线性信号 -> 线性光"""
    a, b, c = 0.17883277, 0.28466892, 0.55991073
    return np.where(nonlinear <= 0.5,
                    (nonlinear ** 2) / 3,
                    (np.exp((nonlinear - c) / a) + b) / 12)

def convert_sdr_to_hdr(rgb_709_nonlinear):
    """
    将 BT.709 非线性 RGB(SDR)转换为 BT.2020 HLG 非线性 RGB(HDR)
    输入/输出均为 [0,1] 范围的非线性信号
    """
    # 1. BT.709 非线性 -> 线性
    linear_709 = bt709_eotf(rgb_709_nonlinear)
    # 2. BT.709 线性 -> BT.2020 线性(基色矩阵变换)
    linear_2020 = M709_to_2020 @ linear_709
    # 3. BT.2020 线性 -> HLG 非线性
    hlg_2020 = bt2020_hlg_oetf(linear_2020)
    return hlg_2020

def convert_hdr_to_sdr(rgb_2020_hlg):
    """
    将 BT.2020 HLG 非线性 RGB(HDR)转换为 BT.709 非线性 RGB(SDR)
    输入/输出均为 [0,1] 范围的非线性信号
    """
    # 1. HLG 非线性 -> 线性
    linear_2020 = bt2020_hlg_eotf(rgb_2020_hlg)
    # 2. BT.2020 线性 -> BT.709 线性
    linear_709 = M2020_to_709 @ linear_2020
    # 3. BT.709 线性 -> 非线性
    rgb_709_nonlinear = bt709_oetf(linear_709)
    return rgb_709_nonlinear

# 示例:SDR 肤色信号(BT.709 非线性)
sdr_color = np.array([0.8, 0.4, 0.3])
hdr_color = convert_sdr_to_hdr(sdr_color)
round_trip = convert_hdr_to_sdr(hdr_color)

print("SDR输入 (BT.709):", sdr_color)
print("HDR输出 (BT.2020 HLG):", np.round(hdr_color, 6))
print("往返还原 (BT.709):", np.round(round_trip, 6))
print("最大误差:", np.max(np.abs(round_trip - sdr_color)))

落地与验证

运行方式:保存为 color_space_convert.py,执行 python color_space_convert.py。需要 numpy 库(pip install numpy)。

验证方法:观察输出中“往返还原”值与原始SDR输入是否一致。由于矩阵变换与传递函数均为数学可逆操作,往返误差应接近浮点精度(约1e-6量级)。若误差在1e-5以下,说明正逆转换自洽,色彩空间转换链路正确。

SDR ↔ HDR 色彩空间转换链路 SDR 输入 BT.709 非线性 RGB BT.709 EOTF 非线性 → 线性光 基色矩阵变换 M709→2020 (3×3) HLG OETF 线性 → 非线性 HDR 输出 BT.2020 HLG RGB 逆变换(HDR → SDR) HLG EOTF → M2020→709 → BT.709 OETF 验证:SDR → HDR → SDR 往返误差 < 1e-5 即通过 代码中 round_trip 与原始 sdr_color 逐通道比较

判定标准

执行后若满足以下条件,则案例通过:

  • 程序无异常退出,输出三行数值与最大误差;
  • “最大误差”小于 1e-5,证明正逆转换数学自洽;
  • HDR输出值域在 [0,1] 内(HLG信号合法范围)。