3dlut 文件格式描述

2024/06/15 Author:daiybh 3dlut

LUT(查找表)是一种将输入颜色值映射到输出颜色值的技术,用于颜色校正和色彩处理。.cube 文件格式是常见的 3D LUT 文件格式之一,用于描述三维查找表。接下来,我将详细解释 .cube 文件中的 LUT 数据部分及其含义。

.cube 文件结构

一个典型的 .cube 文件包括以下部分:

标题和注释(可选):

这些行以 TITLE 开头或以 # 开头,用于描述 LUT 的信息,例如标题和注释。

TITLE "Example LUT"
# This is a comment

域范围(可选):

DOMAIN_MIN 和 DOMAIN_MAX 用于定义输入颜色值的范围,通常为 [0, 1]。如果未指定,默认值为 [0, 1]

DOMAIN_MIN 0.0 0.0 0.0
DOMAIN_MAX 1.0 1.0 1.0

LUT 3D 尺寸:

LUT_3D_SIZE 定义了 3D LUT 的大小。一个 LUT_3D_SIZE N 表示 LUT 有 N * N * N 个条目。

LUT_3D_SIZE 33

LUT 数据:

LUT 数据是颜色映射值列表,描述了每个输入颜色值对应的输出颜色值。这些数据按特定顺序排列,通常是按 Z、Y、X 轴顺序排列。

0.000000 0.000000 0.000000
0.000000 0.000000 0.000032
...
1.000000 1.000000 1.000000

LUT 数据部分的含义

描述

LUT 数据部分是 .cube 文件的核心,包含所有颜色映射条目。每个条目由三个浮点数 (R, G, B) 组成,表示输入颜色对应的输出颜色。

具体含义如下:

  • 每个浮点数范围通常在 [0.0, 1.0] 之间。

  • 数据条目的顺序是按照 Z、Y、X 轴排列。假设 LUT_3D_SIZE N,则:

    • 先遍历 Z 轴,从 0N-1
    • 对于每个 Z 值,遍历 Y 轴,从 0N-1
    • 对于每个 Y 值,遍历 X 轴,从 0N-1

举个例子,如果 LUT_3D_SIZE 2,则数据条目顺序如下:

Z=0, Y=0, X=0 -> 第1个条目
Z=0, Y=0, X=1 -> 第2个条目
Z=0, Y=1, X=0 -> 第3个条目
Z=0, Y=1, X=1 -> 第4个条目
Z=1, Y=0, X=0 -> 第5个条目
Z=1, Y=0, X=1 -> 第6个条目
Z=1, Y=1, X=0 -> 第7个条目
Z=1, Y=1, X=1 -> 第8个条目

每个条目映射输入颜色到输出颜色。例如:

0.000000 0.000000 0.000000

表示输入 (0.0, 0.0, 0.0) 映射到输出 (0.0, 0.0, 0.0)

解释 LUT 应用中的三线性插值

描述

三线性插值是将输入颜色值在 LUT 中插值计算输出颜色值的方法。

假设输入颜色值 (r, g, b)[0, 1] 范围内,

需要在 LUT 中找到对应的输出颜色值:

1. 定位输入值的位置:

  • 将输入值 (r, g, b) 映射到 LUT 的尺寸范围,例如 size - 1

  • 确定输入值在 LUT 中的位置 (x0, y0, z0) 和相邻位置 (x1, y1, z1)

2. 计算插值因子:

计算输入值与位置 x0, y0, z0 的距离,作为插值因子 (tx, ty, tz)

3. 线性插值:

Trilinear_interpolation

  • 首先在 X 轴上插值,计算 (x0, y0, z0)(x1, y0, z0) 之间的值,以及其他相邻位置。
  • 然后在 Y 轴上插值,计算上述结果的插值。
  • 最后在 Z 轴上插值,得到最终插值结果。
  • 通过以上步骤,能在 LUT 中找到精确的输出颜色值,实现高精度的颜色校正。

完整代码

Tips for collapsed sections ### You can add a header You can add text within a collapsed section. You can add an image or a code block, too. ```cpp #include #include #include #include #include #include #include struct Color { float r, g, b; }; class LUT3D { public: LUT3D(const std::string& filename); Color apply(const Color& color) const; private: int size; std::vector<std::vector<std::vector>> lut; void loadCubeFile(const std::string& filename); float clamp(float value, float min, float max) const; Color lerp(const Color& a, const Color& b, float t) const; }; LUT3D::LUT3D(const std::string& filename) { loadCubeFile(filename); } void LUT3D::loadCubeFile(const std::string& filename) { std::ifstream file(filename); if (!file.is_open()) { throw std::runtime_error("Cannot open LUT file."); } std::string line; while (std::getline(file, line)) { if (line.empty() || line[0] == '#') { continue; // Skip comments and empty lines } std::istringstream iss(line); std::string keyword; iss >> keyword; if (keyword == "TITLE") { // Skip title continue; } else if (keyword == "LUT_3D_SIZE") { iss >> size; lut.resize(size, std::vector<std::vector>(size, std::vector(size))); } else if (keyword == "DOMAIN_MIN" || keyword == "DOMAIN_MAX") { // Skip domain information continue; } else { // Put the first value back to the stream iss.seekg(0, std::ios::beg); float r, g, b; iss >> r >> g >> b; if (iss.fail()) { throw std::runtime_error("Invalid LUT data format."); } static int count = 0; int z = count / (size * size); int y = (count / size) % size; int x = count % size; lut[x][y][z] = {r, g, b}; count++; } } file.close(); } float LUT3D::clamp(float value, float min, float max) const { return std::max(min, std::min(value, max)); } Color LUT3D::lerp(const Color& a, const Color& b, float t) const { return {a.r + t * (b.r - a.r), a.g + t * (b.g - a.g), a.b + t * (b.b - a.b)}; } Color LUT3D::apply(const Color& color) const { float r = clamp(color.r, 0.0f, 1.0f) * (size - 1); float g = clamp(color.g, 0.0f, 1.0f) * (size - 1); float b = clamp(color.b, 0.0f, 1.0f) * (size - 1); int x0 = static_cast(r); int y0 = static_cast(g); int z0 = static_cast(b); int x1 = std::min(x0 + 1, size - 1); int y1 = std::min(y0 + 1, size - 1); int z1 = std::min(z0 + 1, size - 1); float tx = r - x0; float ty = g - y0; float tz = b - z0; Color c000 = lut[x0][y0][z0]; Color c100 = lut[x1][y0][z0]; Color c010 = lut[x0][y1][z0]; Color c110 = lut[x1][y1][z0]; Color c001 = lut[x0][y0][z1]; Color c101 = lut[x1][y0][z1]; Color c011 = lut[x0][y1][z1]; Color c111 = lut[x1][y1][z1]; Color c00 = lerp(c000, c100, tx); Color c01 = lerp(c001, c101, tx); Color c10 = lerp(c010, c110, tx); Color c11 = lerp(c011, c111, tx); Color c0 = lerp(c00, c10, ty); Color c1 = lerp(c01, c11, ty); return lerp(c0, c1, tz); } int main() { try { LUT3D lut("path_to_lut_file.cube"); // Sample color to be corrected Color inputColor = {0.5f, 0.5f, 0.5f}; Color outputColor = lut.apply(inputColor); std::cout << "Corrected Color: " << "R: " << outputColor.r << ", G: " << outputColor.g << ", B: " << outputColor.b << std::endl; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; } return 0; } ``` </details>

Search

    Table of Contents