HCL颜色为RGB和后退

时间:2011-09-23 14:31:51

标签: algorithm colors wolfram-mathematica

我需要一种算法将HCL颜色转换为RGB,然后将RGB转换为HCL,同时记住这些颜色空间具有不同的色域(我需要将HCL颜色约束为可以在RGB颜色空间中再现的颜色)。算法是什么(该算法旨在在Wolfram Mathematica中实现,仅支持原生RGB颜色)?我没有处理色彩空间的经验。

P.S。一些关于HCL颜色的文章:

M. Sarifuddin (2005). A new perceptually uniform color space with associated color similarity measure for content–based image and video retrieval.

Zeileis, Hornik and Murrell (2009): Escaping RGBland: Selecting Colors for Statistical Graphics // Computational Statistics & Data Analysis Volume 53, Issue 9, 1 July 2009, Pages 3259-3270

更新 正如Jonathan Jansson所指出的,在上面的两篇文章中,不同的颜色空间用名称“HCL”描述:“第二篇文章使用L C h(uv),它与L u v *但在极坐标中描述,其中h(uv)是u *和v *坐标的角度,C *是该矢量的大小“。所以实际上我需要一种算法将RGB转换为L u v *并向后转换。

6 个答案:

答案 0 :(得分:7)

我也正在学习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。

All source code needed

Good article about how conversion to XYZ works

Nice online converter

但是我不能回答你关于如何将颜色约束到sRGB空间的问题。您可以在转换后丢弃超出0到1范围的RGB颜色。只是夹紧颜色会产生非常奇怪的结果。尝试转到转换器并输入颜色RGB 0 0 255并转换为L * a * b *(类似于L * u * v *),然后将L *增加到70并将其转换回来,结果肯定是不再是蓝色了。

修改:更正了网址 编辑:将另一个答案合并到这个答案中

答案 1 :(得分:6)

我熟悉很多色彩空间,但这个对我来说很新鲜。唉,Mathematica的ColorConvert也不知道。

我找到了一个rgb2hcl例程here,但没有其他常规方法。

可以找到更全面的色彩空间转换包here。它似乎能够与各种颜色空间进行转换。在colorspace_1.1-0.tar.gz \ colorspace_1.1-0.tar \ colorspace \ src中查找文件colorspace.c。请注意,此程序包中的HCL称为PolarLUV。

答案 2 :(得分:3)

HCL是一个非常通用的名称,有很多方法可以获得色调,色度和亮度。例如Chroma.js有一些叫做HCL的东西,它是极坐标转换实验室(当你查看实际的代码时)。其他实现,甚至是从同一站点链接的实现使用Polar Luv。由于您可以简单地借用L因子并通过转换为极坐标来获得色调,因此这些都是获得这三个元素的有效方法。由于混淆因素,将它们称为Polar Lab和Polar Luv要好得多。

微米。 Sarifuddin(2005)的算法不是Polar Luv或Polar Lab,计算上更简单(你不需要先得到Lab或Luv空间),实际上可能更好。本文中有些内容似乎有些不对劲。例如,将欧几里德距离应用于CIE L * C * H *颜色空间。使用Hue意味着它必须是圆形的,只要将该数字插入A²+B²+C²就会给你带来麻烦。将基于色调的色彩空间应用于D94或D00也是如此,因为这些是具有特定于Lab色彩空间的内置校正的距离算法。除非我在那里遗漏了什么,否则我会忽视6-8的数字。我质疑图形中的拒绝容差。您可以设置较低的阈值并做得更好,颜色空间之间的数字不会标准化。无论如何,尽管论文中存在一些看似缺陷,但所描述的算法值得一试。你可能想在RGB上做Euclidean,如果它真的不重要的话。但是,如果您正在购买色距算法,那么就去吧。

这是由M. Sarifuddin在Java中实现的HCL。在重复阅读本文之后,我无法避免这样的结论:它在距离_hcl例程中的色调变化方面将距离缩放了0.16到180.16。这是一个非常重要的因素,几乎不可能是正确的。并使色彩匹配很糟糕。我将纸张行注释掉并使用仅包含Al因子的行。按恒定~1.4因子缩放发光不会使其无法使用。如果没有比例因子,它最终会与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))));
}

答案 3 :(得分:1)

如其他答案中所述,有很多方法可以实现HCL颜色空间并将其映射到RGB。

HSLuv最终成为我使用的东西,并且具有MIT许可的C,C#,Go,Java,PHP和其他几种语言的实现。它类似于CIELUV LCh,但完全映射到RGB。实现are available on GitHub

这是网站上的简短图形,描述了HSLuv颜色空间,其实现输出在右侧的两个面板中:

HSLuv color space examples compared to HSL and CIELUV

答案 4 :(得分:1)

我想在网络上插入颜色,发现 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

答案 5 :(得分:0)

我认为

if (rg <  0) {
    if (gb >= 0) H = 90 + H;
    else { H = H - 90; }
} //works

不是真的有必要,因为atan2(,)而不是atan(/)来自纸张(但现在没有任何关于java atan2(,),尤其是