色彩空间的作用及各空间之间的转换
2024/8/7大约 3 分钟
色彩空间的作用及各空间之间的转换
线性空间和Gamma空间
下面是Unity生成的线性空间和Gamma空间的颜色强度图像,可以看到线性空间下能表征暗部(视觉意义上的暗部)的数值区域十分有限,而Gamma空间下明暗很均匀。


首先要明确两者所描述物理量的不同:
- 线性空间是色彩在物理强度上的线性表达
- Gamma空间是色彩在人眼感知强度上的线性表达
因为人眼对光线的感知强度与光线的物理强度之间是非线性的,想要将感知强度真真正正地记录到数据中需要进行转换。
以物理强度为横轴、感知强度为纵轴,拟合得到一个幂函数曲线y=x^2.2 以感知强度为横轴、物理强度为纵轴,拟合得到一个幂函数曲线y=x^0.4545

附件
1.Unity生成线性空间和Gamma空间图片的自定义窗口
using System.IO;
using UnityEditor;
using UnityEngine;
public class TextureCreator : EditorWindow
{
[MenuItem("QSTX Tools/TextureCreator")]
public static void OpenWindow()
{
GetWindow<TextureCreator>();
}
private const string filefolder = "LinearGamma/";
private string filename = "";
private bool is_gamma = false;
private void OnGUI()
{
//filename = GUILayout.TextField(filename);
is_gamma = GUILayout.Toggle(is_gamma, "is_gamma");
if (GUILayout.Button("生成"))
{
filename = (is_gamma ? "Gamma" : "Linear");
Create(filename,is_gamma);
}
}
void SaveTextureAsPNG(Texture2D texture, string fileName)
{
// 将Texture转换为PNG格式
byte[] bytes = texture.EncodeToPNG();
// 将文件保存到项目文件夹下
string filePath = Path.Combine(Application.dataPath, Path.Combine(filefolder,fileName));
File.WriteAllBytes(filePath, bytes);
Debug.Log("Texture saved to " + filePath);
}
void Create(string filename,bool is_gamma)
{
// 创建一个新的Texture2D对象
int width = 512;
int height = 64;
Texture2D texture = new Texture2D(width, height,TextureFormat.RGBAFloat,0,!is_gamma);
// 填充Texture,设置每个像素的颜色
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
float stren = x / (float)width;
Color strenColor = new Color(stren, stren, stren);
texture.SetPixel(x, y, strenColor);
}
}
// 应用设置的像素颜色
texture.Apply();
// 保存Texture为PNG文件
SaveTextureAsPNG(texture, $"{filename}.png");
AssetDatabase.Refresh();
}
Color GammaToLinearSpace (Color sRGB)
{
// Approximate version from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
return sRGB * (sRGB * (sRGB * 0.305306011f + new Color(0.682171111f,0.682171111f,0.682171111f)) + new Color(0.012522878f,0.012522878f,0.012522878f));
// Precise version, useful for debugging.
//return half3(GammaToLinearSpaceExact(sRGB.r), GammaToLinearSpaceExact(sRGB.g), GammaToLinearSpaceExact(sRGB.b));
}
float GammaToLinearSpaceExact (float value)
{
if (value <= 0.04045F)
return value / 12.92F;
else if (value < 1.0F)
return Mathf.Pow((value + 0.055F)/1.055F, 2.4F);
else
return Mathf.Pow(value, 2.2F);
}
Vector4 Pow(Vector4 v, float p)
{
return new Vector4(Mathf.Pow(v.x, p), Mathf.Pow(v.y, p), Mathf.Pow(v.z, p), Mathf.Pow(v.w, p));
}
Color LinearToGammaSpace (Color linRGB)
{
Vector4 colorVector4 = linRGB;
colorVector4 = Vector4.Max(colorVector4,Vector4.zero);
// An almost-perfect approximation from http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
return Vector4.Max(1.055f * Pow(linRGB, 0.416666667f) - new Vector4(0.055f,0.055f,0.055f,0.055f), Vector4.zero);
// Exact version, useful for debugging.
//return half3(LinearToGammaSpaceExact(linRGB.r), LinearToGammaSpaceExact(linRGB.g), LinearToGammaSpaceExact(linRGB.b));
}
float LinearToGammaSpaceExact (float value)
{
if (value <= 0.0F)
return 0.0F;
else if (value <= 0.0031308F)
return 12.92F * value;
else if (value < 1.0F)
return 1.055F * Mathf.Pow(value, 0.4166667F) - 0.055F;
else
return Mathf.Pow(value, 0.45454545F);
}
}2.Matlab绘制转换函数曲线
% 定义 Gamma 值
gamma_value = 2.2;
% 定义输入亮度值(0 到 1 之间)
x = linspace(0, 1, 256);
% 计算 Gamma 校正后的输出值(Gamma 空间 -> 线性空间)
y_gamma_to_linear = x .^ (1 / gamma_value);
% 计算从线性空间到 Gamma 空间的输出值
y_linear_to_gamma = x .^ gamma_value;
% 绘制曲线
figure; % 创建新图形窗口
hold on; % 保持当前图像,使得可以在同一图上绘制多条曲线
% 绘制 Gamma 到线性空间的转换曲线
plot(x, y_gamma_to_linear, 'r-', 'LineWidth', 2, 'DisplayName', 'Gamma to Linear');
% 绘制线性空间到 Gamma 空间的转换曲线
plot(x, y_linear_to_gamma, 'b--', 'LineWidth', 2, 'DisplayName', 'Linear to Gamma');
% 设置图例位置为左上角
legend('Location', 'northwest');
% 设置 x 轴和 y 轴等长
axis equal;
% 设置图像属性
grid on; % 打开网格
xlabel('Input Intensity'); % x 轴标签
ylabel('Output Intensity'); % y 轴标签
title(['Gamma Correction and Inverse (\gamma = ', num2str(gamma_value), ')']); % 标题,显示 Gamma 值
hold off; % 释放图像