在涂装颜色的颜色空间中查找两种颜色之间的颜色的算法

29

当混合蓝色和黄色颜料时,结果是某种绿色。

我有两个RGB颜色:

蓝色 =(0, 0, 255)

和黄色 =(255, 255, 0)

有什么算法可以找到混合这两种颜色后的RGB颜色呢?所得到的颜色不必非常精确。对于上面的例子,它只需要看起来像某种绿色即可。

提前致谢。

编辑:根据LaC的答案,我用Go编写了此函数,并得到了预期的效果。

func paintMix(c1, c2 image.RGBAColor) image.RGBAColor { 
    r := 255 - ((255 - c1.R) + (255 - c2.R))
    g := 255 - ((255 - c1.G) + (255 - c2.G))
    b := 255 - ((255 - c1.B) + (255 - c2.B))
    return image.RGBAColor{r, g, b, 255}
}

编辑 #2 尽管它成功地混合了青色和黄色,但蓝色和黄色的混合变成了黑色,这似乎不对。我仍在寻找有效的算法。

编辑 #3 Mark Ransom的答案非常好,使用了HLS颜色空间。谢谢Mark Random。

编辑 #4 似乎更好的颜色混合方法是使用Kubelka-Munk方程。


2
这是一个不错的问题。如果有人解决了它,他们可能会在沃尔夫拉姆公司找到工作。http://www.wolframalpha.com/input/?i=yellow+%2B+blue - Seth Moore
1
大部分已经在这里回答了:https://dev59.com/xW855IYBdhLWcg3woV0_ - colinross
1
另一个重复的问题:https://dev59.com/oHM_5IYBdhLWcg3wgjW2 - tylerl
colinross和tylerl,我不同意它们是重复的,因为我指定它不必完全相同,从而排除了CMYK和相关的正确性讨论。 - Alexander
嗨@Alexander - 很棒的问题。你有找到或制作类似Krita的代码吗?我一直在搜索,但我找不到任何开源代码或公式。 - glenstorey
请参考以下链接:http://programmers.stackexchange.com/questions/159830/nearest-color-algorithm-using-hex-triplet - LCJ
5个回答

13

颜料的工作原理是吸收。你从白光(255,255,255)开始,并将其乘以吸收因子。

蓝色油漆吸收所有撞上它的红色和绿色光线。

黄色油漆吸收所有撞上它的蓝色光线。

在理想情况下,这意味着混合黄色和蓝色油漆会得到黑色油漆,或者最好是混浊的灰色。但在实践中,“蓝色”油漆带有一定的绿色偏向,因此你会得到混浊的绿色。我从未见过混合黄色和蓝色能产生令人满意的绿色的例子。维基百科涉及了这个过程的某些复杂性:http://en.wikipedia.org/wiki/Primary_color#Subtractive_primaries

我认为你真正想问的是如何在颜色环上插值颜色。这应该与颜色是吸收性的(如颜料)还是辐射性的(如RGB显示器)无关。

编辑:通过在HSL颜色空间中工作,您可以获得所需的结果。这里有一些用Python实现该算法的代码;平均色调有些棘手,并且基于我之前回答问题的答案for averaging angles

from colorsys import rgb_to_hls,hls_to_rgb
from math import sin,cos,atan2,pi

def average_colors(rgb1, rgb2):
    h1, l1, s1 = rgb_to_hls(rgb1[0]/255., rgb1[1]/255., rgb1[2]/255.)
    h2, l2, s2 = rgb_to_hls(rgb2[0]/255., rgb2[1]/255., rgb2[2]/255.)
    s = 0.5 * (s1 + s2)
    l = 0.5 * (l1 + l2)
    x = cos(2*pi*h1) + cos(2*pi*h2)
    y = sin(2*pi*h1) + sin(2*pi*h2)
    if x != 0.0 or y != 0.0:
        h = atan2(y, x) / (2*pi)
    else:
        h = 0.0
        s = 0.0
    r, g, b = hls_to_rgb(h, l, s)
    return (int(r*255.), int(g*255.), int(b*255.))

>>> average_colors((255,255,0),(0,0,255))
(0, 255, 111)
>>> average_colors((255,255,0),(0,255,255))
(0, 255, 0)
请注意,这个答案模拟颜料混合,原因如上所述。相反,它给出了一种直观的颜色混合方式,没有基于任何物理世界的实际情况。

1
使用HSL对我来说是一个好的选择。我用HSL解决一些问题(比如产生随机对比度颜色集),它给出的结果与人眼的感受非常协调。 - Denys Séguret
1
我尝试在我的UIColor+Mixing类别中模拟真实世界的颜色。它混合了RGB(计算机风格)、RYB(真实世界,加法)和CMYK(真实世界,减法)。算法非常简单,您可以根据颜色数量调整除数以获得更多喜欢的效果。没有限制-尽情享受-https://github.com/ddelruss/UIColor-Mixing。 - Damien Del Russo
@MarkRansom 老实说,我放弃了,并且稍微改变了项目范例。这个问题中存在的复杂性有一篇非常好的文章在这里:http://www.fastcompany.com/3002676/magical-tech-behind-paper-ipads-color-mixing-perfection - glenstorey
@glenstorey,我认为我为另一个问题创建的新算法对于颜色选择器来说非常直观,尽管它需要您选择5个值而不是3个。如果我能找到时间,我会试一试。 - Mark Ransom
@MarkRansom 听起来不错 - 我期待听到你的进展。 - glenstorey
显示剩余2条评论

9
实际上,混合(减法)黄色和青色会得到绿色。黄色是红色+绿色(255, 255, 0),青色是绿色+蓝色(0, 255, 255)。现在制作它们的相反颜色:蓝色(0, 0, 255)和红色(255, 0, 0)。将它们加起来混合,你会得到紫色(255, 0, 255)。再制作它的相反颜色,你会得到绿色(0, 255, 0)。
换句话说,你可以得到一个减法混合,它是你两种颜色相反的加法混合的相反物。

4
RGB颜色空间是基于光的发射,染料和颜料的颜色空间则基于光的吸收

例如,植物之所以看起来是绿色的,并不是因为它们会发出绿光,而是因为它们吸收了所有其他颜色的光,只反射绿色。

基于这一点,您应该能够通过将RGB转换为吸收性颜色空间,进行“混合”,然后再转换回来,从而获得非常接近的结果。


CMYK是标准的吸收式色彩空间。 - blueberryfields
CMYK是常用的吸收式色彩空间之一,也许是最常见的。它足以解决OP问题。 - Bevan

2

你想使用减法CMY色彩(青色,洋红色,黄色)(像LaC所做的那样,不使用该术语)

来回转换很简单:(C,M,Y)=(-R,-G,-B)。
因此,CMY(0,0,0)是白色,而CMY(FF,FF,FF)是黑色。

当您添加两个CMY颜色时,有不同的方法来计算新值。您可以为每个颜色值采用平均值,最大值或介于两者之间的某个值。
例如。对于光滤镜,您总是使用最大值。对于油漆,您可能想使用更接近平均值的值。

正如LaC指出的那样,混合黄色和蓝色并不能得到绿色,但是混合黄色和青色可以。当按最大值混合(例如,光过滤器),黄色和蓝色实际上会产生黑色。这就是为什么您可能希望在混合油漆时使用更接近平均值的值。

除非您想打印某些内容,否则您不应该使用CMYK。黑色“K”颜色主要用于打印机以节省墨水,但无需使用它来表示颜色。


1

使用Convert::Color来生成此类输出:

mauve        is 0xE0B0FF  sRGB=[224,176,255]  HSV=[276, 31,100] 
vermilion    is 0xE34234  sRGB=[227, 66, 52]  HSV=[  5, 77, 89] 
mix          is 0xE2799A  sRGB=[226,121,154]  HSV=[341, 46, 89] 

red          is 0xFF0000  sRGB=[255,  0,  0]  HSV=[  0,100,100] 
blue         is 0x0000FF  sRGB=[  0,  0,255]  HSV=[240,100,100] 
red+blue     is 0x800080  sRGB=[128,  0,128]  HSV=[300,100, 50] 

black        is 0xFFFFFF  sRGB=[255,255,255]  HSV=[  0,  0,100] 
white        is 0x000000  sRGB=[  0,  0,  0]  HSV=[  0,  0,  0] 
grey         is 0x808080  sRGB=[128,128,128]  HSV=[  0,  0, 50] 

dark red     is 0xFF8080  sRGB=[255,128,128]  HSV=[  0, 50,100] 
light red    is 0x800000  sRGB=[128,  0,  0]  HSV=[  0,100, 50] 

pink         is 0x800080  sRGB=[128,  0,128]  HSV=[300,100, 50] 
deep purple  is 0xBF80FF  sRGB=[191,128,255]  HSV=[270, 50,100] 

运行这种代码时:

#!/usr/bin/env perl
use strict;
use warnings;

use Convert::Color;

main();
exit;

sub rgb($$$) {
    my($r, $g, $b) = @_;
    return new Convert::Color:: "rgb8:$r,$g,$b";
}

sub show($$) {
    my ($name, $color) = @_;
    printf "%-12s is 0x%6s", $name,  uc $color->hex;
    printf "  sRGB=[%3d,%3d,%3d] ",     $color->rgb8;

    my ($h,$s,$v) = $color->as_hsv->hsv;
    for ($s, $v) { $_ *= 100 }
    printf " HSV=[%3.0f,%3.0f,%3.0f] ",  $h, $s, $v;
    print "\n";
}

sub main {
    my $vermilion = rgb 227,  66,  52;
    my $mauve     = rgb 224, 176, 255;
    show mauve      => $mauve;
    show vermilion  => $vermilion;

    my $mix = alpha_blend $mauve $vermilion;
    show mix => $mix;
    print "\n";

    my $red   = rgb 255,   0,   0;
    my $blue  = rgb   0,   0, 255;
    show red  => $red;
    show blue => $blue;

    $mix = alpha_blend $red $blue;
    show "red+blue" => $mix;
    print "\n";

    my $black = rgb 255, 255, 255;
    my $white = rgb 0,     0,   0;
    show black => $black;
    show white => $white;

    my $grey  = alpha_blend $black $white;
    show grey  => $grey;
    print "\n";

    my $dark_red  = alpha_blend $red $black;
    my $light_red = alpha_blend $red $white;

    show "dark red"  => $dark_red;
    show "light red" => $light_red;
    print "\n";

    my $magenta = rgb 255, 0, 255;
    my $violet  = rgb 127, 0, 255;

    my $pink        = alpha_blend $magenta $white;
    my $deep_purple = alpha_blend $violet  $black;

    show pink          => $pink;
    show "deep purple" => $deep_purple;;
}

安装Convert::Color和List::UtilsBy后,从http://search.cpan.org/src/PEVANS/List-UtilsBy-0.07/lib/List/UtilsBy.pm下载,运行上述Perl脚本(在末尾添加了缺失的“}”)时,会得到以下结果:在./color.pl第12行,未识别颜色空间名称“rgb8”。 - Alexander
那就是我运行的程序。哦,我看到了,我正在运行 Convert::Color 的 0.08 版本。检查一下你的版本号。 - tchrist
一个回答强制你处理Perl而不是解决问题,这不是我想要的... - Alexander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接