Skip to main content

GOG display characterization with perceptual color difference loss functions

Project description

Loss GOG

基于感知色差损失函数的显示器特性化模型研究。

本项目实现了一种显示器特性化方法:GOG 模型,并研究了不同损失函数(XYZ MSE、CIEDE2000、sUCS)对模型精度的影响。

项目结构

lossgog/
├── src/
│   └── lossgog/                # 核心模块
│       ├── __init__.py
│       ├── data_io.py          # 数据读写
│       ├── plotting.py         # 可视化工具
│       ├── gog/                # GOG 模型
│       │   ├── __init__.py
│       │   ├── model.py        # 模型构建与转换
│       │   └── evaluation.py   # 模型评估
│       ├── lut/                # 3D LUT
│       │   ├── __init__.py
│       │   ├── model.py        # LUT 构建
│       │   └── evaluation.py   # LUT 评估
│       ├── ucs/                # 色差计算
│       │   ├── __init__.py
│       │   ├── de2000.py       # CIEDE2000 色差
│       │   └── sucs.py         # sUCS 色差
│       └── exp/                # 实验模块
│           └── sp_gog.py       
│
├── scripts/                    # 可执行脚本
│   ├── train_gog.py            # 训练 GOG 模型
│   ├── build_lut.py            # 构建 3D LUT
│   ├── train_size_experiment.py # 训练样本量实验
│   ├── gog_roundtrip_test.py   # GOG 往返测试
│   ├── apply_gog.py            # 应用 GOG 到图像
│   ├── apply_lut.py            # 应用 LUT 到图像
│   └── plot_gamut.py           # 绘制色域图
│
├── pyproject.toml              # 项目配置
└── README.md                   

安装

本项目使用 uv 管理依赖:

# 克隆项目
git clone <repo-url>
cd lossgog

# 创建虚拟环境并安装依赖
uv sync

快速开始

# 训练 GOG 模型
uv run scripts/train_gog.py

# 构建 3D LUT
uv run scripts/build_lut.py

# 运行训练样本量实验
uv run scripts/train_size_experiment.py

核心模块详解

1. GOG 模型 (gog/model.py)

GOG (Gain-Offset-Gamma) 模型是一种参数化的显示器色彩特性化模型,共 18 个参数。

数学公式

前向转换 (RGB → XYZ):

$$ L_c = (\text{gain}_c \cdot \text{RGB}_c + \text{offset}_c)^{\gamma_c}, \quad c \in {R, G, B} $$

$$ \begin{bmatrix} X \ Y \ Z \end{bmatrix} = \mathbf{M} \cdot \begin{bmatrix} L_R \ L_G \ L_B \end{bmatrix} $$

逆向转换 (XYZ → RGB):

$$ \begin{bmatrix} L_R \ L_G \ L_B \end{bmatrix} = \mathbf{M}^{-1} \cdot \begin{bmatrix} X \ Y \ Z \end{bmatrix} $$

$$ \text{RGB}_c = \frac{L_c^{1/\gamma_c} - \text{offset}_c}{\text{gain}_c} $$

参数物理意义

参数 含义
gain 输入信号线性放大系数(在非线性变换之前)
offset 黑电平偏移(黑场补偿)
gamma 非线性幂指数(电光传递函数)
matrix 3×3 色彩转换矩阵(RGB 基色到 XYZ 的映射)

两种 GOG 构建方法

本项目提供两种 GOG 模型构建方法:

1. 经典 GOG (classic_gog)

从单通道原色 ramp 测量数据逐通道拟合,适用于有 R/G/B 单独灰阶测量的场景。

原理:

  1. 对每个通道 c ∈ {R, G, B},使用纯色 ramp 数据拟合 tone response: $$L_c = (\text{gain}_c \cdot \text{input} + \text{offset}_c)^{\gamma_c}$$ 其中 $L_c$ 归一化到 [0, 1](基于亮度 Y 减去黑点后归一化)
  2. 矩阵 M 的列直接取各原色最大亮度时的 XYZ(减去黑点贡献)
2. 优化 GOG (make_gog)

使用所有测量数据联合优化 18 个参数,支持多种损失函数。

原理:

  1. 初始化参数(gain=1, offset=0.001, gamma=2.2, matrix=缩放单位阵)
  2. 使用 L-BFGS-B 优化器最小化预测 XYZ 与测量 XYZ 的误差
  3. 支持三种损失函数:XYZ MSE、CIEDE2000、sUCS

关键函数

classic_gog(rgb, xyz, ramp_indices=None, verbose=True)

从单通道原色 ramp 数据构建经典 GOG 模型。

参数:

  • rgb: (N, 3) 数组,RGB 值,范围 [0, 1]
  • xyz: (N, 3) 数组,对应的 XYZ 三刺激值
  • ramp_indices: 可选,指定每个通道的数据索引范围
    • [(0, 18), (18, 36), (36, 54)] 表示 R/G/B 各 18 个样本
    • 若为 None,自动检测单通道数据
  • verbose: 是否打印拟合过程

返回:

{
    "gain": np.array([g_R, g_G, g_B]),
    "offset": np.array([o_R, o_G, o_B]),
    "gamma": np.array([γ_R, γ_G, γ_B]),
    "matrix": np.array([[...], [...], [...]])  # 列为 R/G/B 原色 XYZ
}

示例:

from lossgog.gog import classic_gog, rgb_to_xyz_gog

# 从原色 ramp 构建经典 GOG
ramp_indices = [(0, 18), (18, 36), (36, 54)]  # R/G/B 各 18 点
gog_model = classic_gog(rgb_train, xyz_train, ramp_indices=ramp_indices)

# 使用模型
xyz_pred = rgb_to_xyz_gog(rgb_test, gog_model)
make_gog(rgb, xyz, mode="xyz", verbose=True)

从 RGB-XYZ 测量数据拟合 GOG 模型参数。

参数:

  • rgb: (N, 3) 数组,RGB 值,范围 [0, 1]
  • xyz: (N, 3) 数组,对应的 XYZ 三刺激值
  • mode: 损失函数类型
    • "xyz": XYZ 空间均方误差(最快)
    • "de2000": CIEDE2000 色差(感知均匀)
    • "sucs": sUCS 色差(计算快于 DE2000)
  • verbose: 是否打印优化过程

返回:

{
    "gain": np.array([g_R, g_G, g_B]),      # 增益
    "offset": np.array([o_R, o_G, o_B]),   # 偏移
    "gamma": np.array([γ_R, γ_G, γ_B]),    # 伽马
    "matrix": np.array([[...], [...], [...]]) # 3×3 矩阵
}

示例:

from lossgog.gog import make_gog, rgb_to_xyz_gog

# 训练模型
gog_model = make_gog(rgb_train, xyz_train, mode="de2000")

# 使用模型
xyz_pred = rgb_to_xyz_gog(rgb_test, gog_model)
rgb_to_xyz_gog(rgb, gog_model)

使用 GOG 模型将 RGB 转换为 XYZ。

xyz_to_rgb_gog(xyz, gog_model)

使用 GOG 模型的逆变换将 XYZ 转换为 RGB。


2. 3D LUT (lut/model.py)

3D LUT 是一种基于查表和插值的色彩转换方法,适用于任意非线性映射。

Mid Space(中间色彩空间)

由于测量数据在 XYZ 空间中分布不均匀,直接插值效果不佳。本项目引入 Mid Space 作为中间空间:

$$ \text{Mid Space} = \text{EOTF}^{-1}{\text{BT.1886}} \left( \text{XYZ_to_RGB}{\text{BT.2020}} \left( \frac{\text{XYZ}}{Y_{\text{white}}} \right) \right) $$

设计原理:

  1. 白点归一化:除以白点亮度 $Y_{\text{white}}$,消除绝对亮度的影响
  2. BT.2020 RGB:宽色域空间,减少色域裁剪
  3. 逆 EOTF:类似伽马编码,使数值分布更均匀

关键函数

build_forward_lut(source_rgb, target_xyz, source_grid_size, output_size=17)

从规则网格测量数据构建前向 LUT (RGB → XYZ)。

参数:

  • source_rgb: (N, 3) RGB 测量点,必须形成规则网格
  • target_xyz: (N, 3) 对应的 XYZ 值
  • source_grid_size: 源数据每通道采样数(如 15 表示 15³=3375 点)
  • output_size: 输出 LUT 尺寸(如 17, 33, 65)

返回:

  • (output_size, output_size, output_size, 3) 数组

示例:

from lossgog.lut import build_forward_lut
import colour

# 从 15³ 测量点构建 17³ LUT
lut_data = build_forward_lut(rgb, xyz, source_grid_size=15, output_size=17)
lut = colour.LUT3D(table=lut_data, size=17)

# 保存为 .cube 文件
colour.write_LUT(lut, "output.cube")
build_inverse_lut(measured_xyz, measured_rgb, white_point_luminance, output_size=17, method="linear")

从散点测量数据构建逆向 LUT (Mid Space → RGB)。

参数:

  • measured_xyz: (N, 3) XYZ 测量值
  • measured_rgb: (N, 3) 对应的 RGB 值
  • white_point_luminance: 白点亮度(Y 值)
  • output_size: 输出 LUT 尺寸
  • method: 插值方法
    • "linear": 线性插值(快速,可能有空洞)
    • "nearest": 最近邻(最快)
    • "rbf": 径向基函数(平滑,较慢)

使用逆向 LUT:

from lossgog.lut import build_inverse_lut, xyz_to_mid_space

# 构建 LUT
lut_data = build_inverse_lut(xyz, rgb, white_Y, method="rbf")
inverse_lut = colour.LUT3D(table=lut_data)

# 使用 LUT:XYZ → Mid Space → RGB
mid = xyz_to_mid_space(target_xyz, white_Y)
display_rgb = inverse_lut.apply(mid)
xyz_to_mid_space(xyz, white_point_luminance)

将 XYZ 转换为 Mid Space。

mid_space_to_xyz(mid, white_point_luminance)

将 Mid Space 转换回 XYZ。


3. 色差计算 (ucs/)

calculate_de2000(xyz_pred, xyz_target)

计算 CIEDE2000 色差。

参数:

  • xyz_pred, xyz_target: (N, 3) XYZ 值,范围 [0, 1]

返回:

  • (N,) 色差值数组

calculate_de_sucs(xyz_pred, xyz_target)

计算 sUCS (Simple Uniform Color Space) 色差,比 CIEDE2000 计算更快。


4. 数据读写 (data_io.py)

read_cs2000_csv(file_path, spectral_length=401)

读取 CS2000 色度计的测量数据 CSV 文件。

CSV 格式:

R, G, B, X, Y, Z, 380nm, 381nm, ..., 780nm

返回:

{
    "RGB": np.array(..., dtype=np.uint8),    # (N, 3)
    "XYZ": np.array(..., dtype=np.float32),  # (N, 3)
    "spectral": np.array(..., dtype=np.float32)  # (N, 401)
}

实验脚本

训练样本量实验 (train_size_experiment.py)

研究训练样本数量对模型精度的影响:

  • 从 10 到 1000 点对数间隔采样
  • 对比 XYZ MSE、CIEDE2000、sUCS 三种损失函数
  • 每个样本量重复 3 次取平均
  • 输出平均色差随样本量变化的曲线

许可证

MIT License


作者

Miaosen Zhou

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lossgog-0.1.1.tar.gz (66.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lossgog-0.1.1-py3-none-any.whl (29.8 kB view details)

Uploaded Python 3

File details

Details for the file lossgog-0.1.1.tar.gz.

File metadata

  • Download URL: lossgog-0.1.1.tar.gz
  • Upload date:
  • Size: 66.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for lossgog-0.1.1.tar.gz
Algorithm Hash digest
SHA256 98f27751a61a63ef26a00b389898048a85e4bd82c5d67bc6939d0a9b4c5b0369
MD5 db2dc3591f54a852d5463befe9f77343
BLAKE2b-256 9bfd5b3206009584a62f8a0b72490e809714c6c7aab370e3c569c5b49fe43ea7

See more details on using hashes here.

File details

Details for the file lossgog-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: lossgog-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 29.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for lossgog-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 da4277dbd257b8ef5cf93d30a4fa456b4915c788f64c7a86d1125a9e80cf8511
MD5 aacc4a1ba2182bda4b42507364a8d4bd
BLAKE2b-256 10d1a178ecb15c3fa09d6636b9f20be656b61f76212d8da00695ea8f23da7a12

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page