LAB 到 RGB 使用 Lcms2 给出奇怪的结果
LAB to RGB using Lcms2 gives odd result
我正在做一个需要做 lab->rgb 的项目。
我正在使用名为 lcms2 的色彩管理库,link: https://github.com/mm2/Little-CMS
我的项目是在 c# 中,我使用 interop 与 dll 进行通信。该项目必须 运行 linux Ubuntu Core 16,大多数 c# 颜色管理库都不支持,这就是我使用 lcms2 的原因。
尝试转换 LAB L:99、A:-122、B:-66 时,我得到了非常奇怪的结果,RGB 值:
R:-3769.14044380188
客服:304.88466560840607
B: 378.99470329284668
这是(简化的)数据类,以及测试中使用的扩展方法,当然还有测试:
[Test()]
public void LabThatGaveWeirdResultDoesntDoItAnymore()
{
var lab = new LabColor(99, -112, -66);
var rgb = lab.ToRgb();
Assert.IsTrue(new[] { rgb.R, rgb.G, rgb.B }.All(v => v >= 0 && v <= 255));
}
public class LabColor
{
public double L { get; }
public double A { get; }
public double B { get; }
public LabColor(int l, int a, int b)
{
L = l;
A = a;
B = b;
}
}
public class RgbColor
{
public double R { get; }
public double G { get; }
public double B { get; }
public RgbColor(double r, double g, double b)
{
R = r;
G = g;
B = b;
}
}
public static RgbColor ToRgb(this LabColor lab, Intent intent = Intent.Perceptual)
{
var labProfile = Lcms2.GetLabProfile();
var hRgbProfile = Lcms2.GetRgbProfile();
var transform = Lcms2.GetTransform(labProfile, hRgbProfile, intent);
var rgbBuffer = new double[3];
var labBuffer = new[]
{
lab.L,
lab.A,
lab.B
};
Lcms2.Transform(transform, labBuffer, ref rgbBuffer, 1);
Lcms2.FreeMemory(labProfile);
Lcms2.FreeMemory(hRgbProfile);
Lcms2.FreeMemory(transform);
return new RgbColor(rgbBuffer[0] * 255, rgbBuffer[1] * 255, rgbBuffer[2] * 255);
}
lcms2 封装代码:
internal enum ProfileFormat
{
Rgb = 4456472,
Cmyk = 4587552,
Lab = 4849688
}
public enum Intent
{
Perceptual = 0,
RelativeColorimetric = 1,
Saturation = 2,
AbsoluteColorimetric = 3
}
internal sealed class Profile
{
public Profile(IntPtr pointer, ProfileFormat format)
{
Pointer = pointer;
Format = format;
}
internal ProfileFormat Format { get; }
internal IntPtr Pointer { get; }
}
internal sealed class Transform
{
public Transform(IntPtr pointer)
{
Pointer = pointer;
}
internal IntPtr Pointer { get; }
}
internal sealed class Lcms2
{
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCreate_sRGBProfile")]
static extern IntPtr cmsCreate_sRGBProfile();
public static Profile GetRgbProfile()
{
return new Profile(cmsCreate_sRGBProfile(), ProfileFormat.Rgb);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCreateLab4Profile")]
static extern IntPtr cmsCreateLab4Profile();
public static Profile GetLabProfile()
{
return new Profile(cmsCreateLab4Profile(), ProfileFormat.Lab);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCreateTransform")]
static extern IntPtr cmsCreateTransform(IntPtr inProfilePtr, uint inputFormat, IntPtr outPutProfilePtr, uint outputFormat, uint intent, uint dword);
public static Transform GetTransform(Profile inProfile, Profile outProfile, Intent intent)
{
return new Transform(cmsCreateTransform(inProfile.Pointer, (uint)inProfile.Format, outProfile.Pointer, (uint)outProfile.Format, (uint)intent, 0));
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsDoTransform")]
static extern void cmsDoTransform(IntPtr transformPtr, double[] inputBuffer, double[] outputBuffer, uint size);
public static void Transform(Transform transform, double[] inputBuffer, ref double[] outputBuffer, uint size)
{
cmsDoTransform(transform.Pointer, inputBuffer, outputBuffer, size);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsDeleteTransform")]
static extern void cmsDeleteTransform(IntPtr transformPtr);
public static void FreeMemory(Transform transform)
{
cmsDeleteTransform(transform.Pointer);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCloseProfile")]
static extern void cmsCloseProfile(IntPtr profilePtr);
public static void FreeMemory(Profile profile)
{
cmsCloseProfile(profile.Pointer);
}
我通过在 c++ lcms2 项目中重新创建代码来计算 profileformat 整数。
他们被定义为
#define TYPE_RGB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0))
#define TYPE_Lab_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0))
我模仿了代码,创建了:
uint FLOAT_SH(uint i) => (i) << 22;
uint COLORSPACE_SH(uint i) => (i) << 16;
uint PT_Lab = 10;
uint PT_RGB = 4;
uint CHANNELS_SH(uint i) => (i) << 3;
uint BYTES_SH(uint i) => (i);
var TYPE_RGB_DBL = (FLOAT_SH(1) | COLORSPACE_SH(PT_RGB) | CHANNELS_SH(3) | BYTES_SH(0)); //4456472
var TYPE_Lab_DBL = (FLOAT_SH(1) | COLORSPACE_SH(PT_Lab) | CHANNELS_SH(3) | BYTES_SH(0)); //4849688
恰好 Lab (99, -122, -66) 超出了 sRGB 色域。您不能在 sRGB 中表示此颜色。如果裁剪值是 RGB=(0, 255, 255),但这不是您要求的颜色。
否则 sRGB 的无限值是:
C:\github\Little-CMS\bin>transicc -t1 -i*Lab
LittleCMS ColorSpace 转换计算器 - 4.3 [LittleCMS 2.08]
输入值,'q'退出
大号*? 99
一种*? -122
乙*? -66
R=-4111.8278 G=307.7362 B=378.8372
我正在做一个需要做 lab->rgb 的项目。 我正在使用名为 lcms2 的色彩管理库,link: https://github.com/mm2/Little-CMS
我的项目是在 c# 中,我使用 interop 与 dll 进行通信。该项目必须 运行 linux Ubuntu Core 16,大多数 c# 颜色管理库都不支持,这就是我使用 lcms2 的原因。
尝试转换 LAB L:99、A:-122、B:-66 时,我得到了非常奇怪的结果,RGB 值:
R:-3769.14044380188 客服:304.88466560840607 B: 378.99470329284668
这是(简化的)数据类,以及测试中使用的扩展方法,当然还有测试:
[Test()]
public void LabThatGaveWeirdResultDoesntDoItAnymore()
{
var lab = new LabColor(99, -112, -66);
var rgb = lab.ToRgb();
Assert.IsTrue(new[] { rgb.R, rgb.G, rgb.B }.All(v => v >= 0 && v <= 255));
}
public class LabColor
{
public double L { get; }
public double A { get; }
public double B { get; }
public LabColor(int l, int a, int b)
{
L = l;
A = a;
B = b;
}
}
public class RgbColor
{
public double R { get; }
public double G { get; }
public double B { get; }
public RgbColor(double r, double g, double b)
{
R = r;
G = g;
B = b;
}
}
public static RgbColor ToRgb(this LabColor lab, Intent intent = Intent.Perceptual)
{
var labProfile = Lcms2.GetLabProfile();
var hRgbProfile = Lcms2.GetRgbProfile();
var transform = Lcms2.GetTransform(labProfile, hRgbProfile, intent);
var rgbBuffer = new double[3];
var labBuffer = new[]
{
lab.L,
lab.A,
lab.B
};
Lcms2.Transform(transform, labBuffer, ref rgbBuffer, 1);
Lcms2.FreeMemory(labProfile);
Lcms2.FreeMemory(hRgbProfile);
Lcms2.FreeMemory(transform);
return new RgbColor(rgbBuffer[0] * 255, rgbBuffer[1] * 255, rgbBuffer[2] * 255);
}
lcms2 封装代码:
internal enum ProfileFormat
{
Rgb = 4456472,
Cmyk = 4587552,
Lab = 4849688
}
public enum Intent
{
Perceptual = 0,
RelativeColorimetric = 1,
Saturation = 2,
AbsoluteColorimetric = 3
}
internal sealed class Profile
{
public Profile(IntPtr pointer, ProfileFormat format)
{
Pointer = pointer;
Format = format;
}
internal ProfileFormat Format { get; }
internal IntPtr Pointer { get; }
}
internal sealed class Transform
{
public Transform(IntPtr pointer)
{
Pointer = pointer;
}
internal IntPtr Pointer { get; }
}
internal sealed class Lcms2
{
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCreate_sRGBProfile")]
static extern IntPtr cmsCreate_sRGBProfile();
public static Profile GetRgbProfile()
{
return new Profile(cmsCreate_sRGBProfile(), ProfileFormat.Rgb);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCreateLab4Profile")]
static extern IntPtr cmsCreateLab4Profile();
public static Profile GetLabProfile()
{
return new Profile(cmsCreateLab4Profile(), ProfileFormat.Lab);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCreateTransform")]
static extern IntPtr cmsCreateTransform(IntPtr inProfilePtr, uint inputFormat, IntPtr outPutProfilePtr, uint outputFormat, uint intent, uint dword);
public static Transform GetTransform(Profile inProfile, Profile outProfile, Intent intent)
{
return new Transform(cmsCreateTransform(inProfile.Pointer, (uint)inProfile.Format, outProfile.Pointer, (uint)outProfile.Format, (uint)intent, 0));
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsDoTransform")]
static extern void cmsDoTransform(IntPtr transformPtr, double[] inputBuffer, double[] outputBuffer, uint size);
public static void Transform(Transform transform, double[] inputBuffer, ref double[] outputBuffer, uint size)
{
cmsDoTransform(transform.Pointer, inputBuffer, outputBuffer, size);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsDeleteTransform")]
static extern void cmsDeleteTransform(IntPtr transformPtr);
public static void FreeMemory(Transform transform)
{
cmsDeleteTransform(transform.Pointer);
}
[DllImport("lcms2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "cmsCloseProfile")]
static extern void cmsCloseProfile(IntPtr profilePtr);
public static void FreeMemory(Profile profile)
{
cmsCloseProfile(profile.Pointer);
}
我通过在 c++ lcms2 项目中重新创建代码来计算 profileformat 整数。 他们被定义为
#define TYPE_RGB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0))
#define TYPE_Lab_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0))
我模仿了代码,创建了:
uint FLOAT_SH(uint i) => (i) << 22;
uint COLORSPACE_SH(uint i) => (i) << 16;
uint PT_Lab = 10;
uint PT_RGB = 4;
uint CHANNELS_SH(uint i) => (i) << 3;
uint BYTES_SH(uint i) => (i);
var TYPE_RGB_DBL = (FLOAT_SH(1) | COLORSPACE_SH(PT_RGB) | CHANNELS_SH(3) | BYTES_SH(0)); //4456472
var TYPE_Lab_DBL = (FLOAT_SH(1) | COLORSPACE_SH(PT_Lab) | CHANNELS_SH(3) | BYTES_SH(0)); //4849688
恰好 Lab (99, -122, -66) 超出了 sRGB 色域。您不能在 sRGB 中表示此颜色。如果裁剪值是 RGB=(0, 255, 255),但这不是您要求的颜色。
否则 sRGB 的无限值是:
C:\github\Little-CMS\bin>transicc -t1 -i*Lab LittleCMS ColorSpace 转换计算器 - 4.3 [LittleCMS 2.08]
输入值,'q'退出 大号*? 99 一种*? -122 乙*? -66
R=-4111.8278 G=307.7362 B=378.8372