[英]HCL color to RGB and backward
我需要一种算法来将 HCL 颜色转换为 RGB 并将反向 RGB 转换为 HCL,记住这些颜色空间具有不同的色域(我需要将 HCL 颜色限制为可以在 RGB 颜色空间中再现的颜色)。 这个算法是什么(该算法旨在在Wolfram Mathematica中实现,它只支持原生的 RGB 颜色)? 我没有处理色彩空间的经验。
PS一些关于HCL颜色的文章:
M. Sarifuddin (2005)。 用于基于内容的图像和视频检索的具有相关颜色相似性度量的新的感知均匀颜色空间。
更新:正如Jonathan Jansson所指出的,在以上两篇文章中,不同的色彩空间用名称“HCL”描述:“第二篇文章使用 L C h(uv),与Lu v* 相同,但在极坐标中描述坐标 其中 h(uv) 是 u* 和 v* 坐标的角度,C* 是该向量的大小"。 所以实际上我需要一种算法来将 RGB 转换为 L u v* 并反向转换。
我也在学习 HCL 色彩空间。 不过,您问题中两篇文章中使用的色彩空间似乎是不同的色彩空间。
第二篇文章使用 L*C*h(uv),它与 L*u*v* 相同,但在极坐标中描述,其中 h(uv) 是 u* 和 v* 坐标的角度,C* 是幅度那个向量。
第一篇文章中的 LCH 颜色空间似乎描述了另一种颜色空间,而不是使用更算法转换的颜色空间。 这里还有第一篇论文的另一个版本: http : //isjd.pdii.lipi.go.id/admin/jurnal/14209102121.pdf
如果您打算使用 CIE L*u*v*,您需要先将 sRGB 转换为 CIE XYZ,然后再转换为 CIE L*u*v*。 RGB 在大多数情况下实际上是指 sRGB,因此无需从 RGB 转换为 sRGB。
但是我无法回答您关于如何将颜色限制到 sRGB 空间的问题。 您可以在转换后丢弃 0 到 1 范围之外的 RGB 颜色。 仅钳制颜色会产生非常奇怪的结果。 尝试转到转换器并输入颜色 RGB 0 0 255 并转换为 L*a*b*(类似于 L*u*v*),然后将 L* 增加到说 70 并将其转换回来,结果肯定是不再蓝色了。
编辑:更正了 URL 编辑:将另一个答案合并到此答案中
HCL 是一个非常通用的名称,可以通过多种方式获得色调、色度和亮度。 例如,Chroma.js 有一个叫做 HCL 的东西,它是极坐标转换实验室(当你查看实际代码时)。 其他实现,即使是从同一站点链接的实现也使用 Polar Luv。 由于您可以简单地借用 L 因子并通过转换为极坐标来导出色调,因此这些都是获得这三个元素的有效方法。 由于混淆因素,将它们称为 Polar Lab 和 Polar Luv 会好得多。
M. Sarifuddin (2005) 的算法不是 Polar Luv 或 Polar Lab,并且在计算上更简单(您不需要先推导出 Lab 或 Luv 空间),实际上可能更好。 论文中有些地方似乎是错误的。 例如,将欧几里得距离应用于 CIE L*C*H* 色彩空间。 使用 Hue 意味着它必须是圆形的,只是将这个数字塞进 A²+B²+C² 会给你带来问题。 将基于色调的色彩空间应用于 D94 或 D00 也是如此,因为这些是距离算法,具有特定于 Lab 色彩空间的内置校正。 除非我在那里遗漏了什么,否则我会忽略数字 6-8。 我质疑图形中的拒绝容差。 您可以设置较低的阈值并做得更好,并且颜色空间之间的数字未标准化。 无论如何,尽管论文中似乎存在一些缺陷,但所描述的算法值得一试。 如果不是很重要,您可能想要在 RGB 上进行欧几里得运算。 但是,如果您正在四处寻找颜色距离算法,那么您就可以了。
这是由 M. Sarifuddin 给出的用 Java 实现的 HCL。 反复阅读这篇论文后,我无法避免得出这样的结论,即它根据 distance_hcl 例程中的色调变化将距离缩放了 0.16 到 180.16 之间的系数。 这是一个如此深刻的因素,几乎不可能是正确的。 并使配色变得糟糕。 我已经注释掉了论文的行,并使用了仅包含 Al 因子的行。 以常数 ~1.4 因子缩放 Luminescence 不会使其无法使用。 没有任何比例因子,它最终与 cycldistance 相同。
http://w3.uqo.ca/missaoui/Publications/TRColorSpace.zip是论文的修正和改进版本。
static final public double Y0 = 100;
static final public double gamma = 3;
static final public double Al = 1.4456;
static final public double Ach_inc = 0.16;
public void rgb2hcl(double[] returnarray, int r, int g, int b) {
double min = Math.min(Math.min(r, g), b);
double max = Math.max(Math.max(r, g), b);
if (max == 0) {
returnarray[0] = 0;
returnarray[1] = 0;
returnarray[2] = 0;
return;
}
double alpha = (min / max) / Y0;
double Q = Math.exp(alpha * gamma);
double rg = r - g;
double gb = g - b;
double br = b - r;
double L = ((Q * max) + ((1 - Q) * min)) / 2;
double C = Q * (Math.abs(rg) + Math.abs(gb) + Math.abs(br)) / 3;
double H = Math.toDegrees(Math.atan2(gb, rg));
/*
//the formulae given in paper, don't work.
if (rg >= 0 && gb >= 0) {
H = 2 * H / 3;
} else if (rg >= 0 && gb < 0) {
H = 4 * H / 3;
} else if (rg < 0 && gb >= 0) {
H = 180 + 4 * H / 3;
} else if (rg < 0 && gb < 0) {
H = 2 * H / 3 - 180;
} // 180 causes the parts to overlap (green == red) and it oddly crumples up bits of the hue for no good reason. 2/3H and 4/3H expanding and contracting quandrants.
*/
if (rg < 0) {
if (gb >= 0) H = 90 + H;
else { H = H - 90; }
} //works
returnarray[0] = H;
returnarray[1] = C;
returnarray[2] = L;
}
public double cycldistance(double[] hcl1, double[] hcl2) {
double dL = hcl1[2] - hcl2[2];
double dH = Math.abs(hcl1[0] - hcl2[0]);
double C1 = hcl1[1];
double C2 = hcl2[1];
return Math.sqrt(dL*dL + C1*C1 + C2*C2 - 2*C1*C2*Math.cos(Math.toRadians(dH)));
}
public double distance_hcl(double[] hcl1, double[] hcl2) {
double c1 = hcl1[1];
double c2 = hcl2[1];
double Dh = Math.abs(hcl1[0] - hcl2[0]);
if (Dh > 180) Dh = 360 - Dh;
double Ach = Dh + Ach_inc;
double AlDl = Al * Math.abs(hcl1[2] - hcl2[2]);
return Math.sqrt(AlDl * AlDl + (c1 * c1 + c2 * c2 - 2 * c1 * c2 * Math.cos(Math.toRadians(Dh))));
//return Math.sqrt(AlDl * AlDl + Ach * (c1 * c1 + c2 * c2 - 2 * c1 * c2 * Math.cos(Math.toRadians(Dh))));
}
正如其他答案中提到的,有很多方法可以实现 HCL 色彩空间并将其映射到 RGB。
HSLuv最终成为了我所使用的,并且在 C、C#、Go、Java、PHP 和其他几种语言中有 MIT 许可的实现。 它类似于 CIELUV LCh,但完全映射到 RGB。 实现可在 GitHub 上找到。
这是来自网站的描述 HSLuv 颜色空间的简短图形,右侧两个面板中具有实现输出:
我想在网上插入颜色,发现 HCL 是最合适的颜色空间,我找不到任何使转换简单且高效的库,所以我写了自己的。
有许多常数在起作用,其中一些常数会因您的来源而有很大差异。
我的目标是网络,我想我最好匹配铬源代码。 这是一个用 Typescript 编写的最小化片段,sRGB XYZ 矩阵是预先计算的,并且所有常量都是内联的。
const rgb255 = (v: number) => (v < 255 ? (v > 0 ? v : 0) : 255);
const b1 = (v: number) => (v > 0.0031308 ? v ** (1 / 2.4) * 269.025 - 14.025 : v * 3294.6);
const b2 = (v: number) => (v > 0.2068965 ? v ** 3 : (v - 4 / 29) * (108 / 841));
const a1 = (v: number) => (v > 10.314724 ? ((v + 14.025) / 269.025) ** 2.4 : v / 3294.6);
const a2 = (v: number) => (v > 0.0088564 ? v ** (1 / 3) : v / (108 / 841) + 4 / 29);
function fromHCL(h: number, c: number, l: number): RGB {
const y = b2((l = (l + 16) / 116));
const x = b2(l + (c / 500) * Math.cos((h *= Math.PI / 180)));
const z = b2(l - (c / 200) * Math.sin(h));
return [
rgb255(b1(x * 3.021973625 - y * 1.617392459 - z * 0.404875592)),
rgb255(b1(x * -0.943766287 + y * 1.916279586 + z * 0.027607165)),
rgb255(b1(x * 0.069407491 - y * 0.22898585 + z * 1.159737864)),
];
}
function toHCL(r: number, g: number, b: number) {
const y = a2((r = a1(r)) * 0.222488403 + (g = a1(g)) * 0.716873169 + (b = a1(b)) * 0.06060791);
const l = 500 * (a2(r * 0.452247074 + g * 0.399439023 + b * 0.148375274) - y);
const q = 200 * (y - a2(r * 0.016863605 + g * 0.117638439 + b * 0.865350722));
const h = Math.atan2(q, l) * (180 / Math.PI);
return [h < 0 ? h + 360 : h, Math.sqrt(l * l + q * q), 116 * y - 16];
}
这是上述代码段的游乐场。
它包括 d3 的 interpolateHCL 和用于比较的浏览器原生 css 转换。
https://svelte.dev/repl/0a40a8348f8841d0b7007c58e4d9b54c
这是与任何 Web 颜色格式之间进行转换并将其插入 HCL 颜色空间的要点。
https://gist.github.com/pushkine/c8ba98294233d32ab71b7e19a0ebdbb9
我认为
if (rg < 0) { if (gb >= 0) H = 90 + H; else { H = H - 90; } } //works
不是真的有必要因为 atan2(,) 而不是 atan(/) 来自纸(但现在不要任何关于 java atan2(,) 特别是
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.