是否有一种颜色混合算法可以像混合真实颜色一样工作?

发布于 2024-08-03 03:08:58 字数 3816 浏览 11 评论 0原文

常见的 RGB 颜色混合与绘画颜色的混合有很大不同, 它是光的混合而不是颜料的混合。

例如:(

Blue (0,0,255) + Yellow (255,255,0) = Grey (128,128,128)

应该是蓝色+黄色=绿色)

是否有任何已知的颜色混合算法可以像混合真实颜色一样工作?


方法

我已经尝试过以下

转换两种颜色到 HSV 并混合色调(乘以根据饱和度计算的系数), 以及饱和度和价值通道的简单平均值。然后我计算了两者的平均亮度 颜色并调整生成的颜色以匹配此亮度。 这工作得很好,但色调混合有时是错误的,例如:

Red (Hue 0°) + Blue (Hue 240°) = Green (Hue 120°)

我发现有时我需要将色调值移动 360°(当 色调大于180°)。

Red (Hue 360°) + Blue (Hue 240°) = Magenta/fuchsia (Hue 300°)

但这种转变也不是很好,例如:(

Cyan (Hue 179°) + Red (Hue 0°) = Hue 89.5°
Cyan (Hue 181°) + Red (Hue 0°) --> shifting is performed (the difference is greater than 180°)
Cyan (Hue 181°) + Red (Hue 360°) = Hue 270.5°

色调 179 + 红色)和(色调 181 + 红色)会产生两种完全不同的颜色。


然后我尝试了 CIE Lab 色彩空间(如在 Photoshop 中),它的设计更接近人类感知颜色的方式。

我只对相应的两个通道使用了简单的平均值,但结果并不令人满意, 例如,我从蓝色 (98, -16, 93) 和黄色 (30, 68, -112) 中得到粉色 (64, 26, -9.5)。这些系数 取自 Photoshop。

也许如果我使用一些与平均不同的操作,它可以工作,但是 我不知道是什么。


CMYK 也不起作用,结果与 RGB 或 LAB 中的结果一样。


似乎在任何这些色彩空间中简单的加法或减法颜色混合都不会产生自然的结果。


工作实现

Krita – 绘画混合器

光栅图形编辑器 Krita 在某些时候有一个更真实的颜色混合的工作实现: http://commit-digest.org/issues/2007-08-12/(Painterly 混合器插件)

他们说这是第一个使用 Kubelka 和 Munk 方程来实现特殊技术的公共应用程序,这些方程描述了颜料的行为。

以下是 Krita 混色视频:https://www.youtube.com/watch?v=lyLPZDVdQiQ

FiftyThree 的论文

还有 关于 iOS 版 Paper 应用程序中的颜色混合的文章,由 FiftyThree 开发。他们描述了他们如何在该领域进行创新和实验,并提供了混合蓝色和黄色产生绿色的样本。然而,那里并没有真正描述实际的过程或算法。

引用:

“在寻找良好的混合算法时,我们最初尝试在各种颜色空间之间进行插值:RGB、HSV 和 HSL,然后是 CieLAB 和 CieLUV。结果令人失望,”Chen 说道。 “我们知道红色和黄色应该产生橙色,或者红色和蓝色应该产生紫色 - 但无论您使用什么颜色空间,都没有任何方法可以得到这些颜色。有一条工程公理:做最简单的事情好吧,我们现在已经尝试了最简单的方法,但他们感觉不太对。”

看起来和 Krita 一样,Paper 也实现了 Kubelka-Munk 模型:

[...] Kubelka-Munk 模型的每种颜色至少有六个值,包括每种 RGB 颜色的反射和吸收值。 “虽然屏幕上颜色的外观可以在三维空间中描述,但颜色的混合实际上是在六维空间中发生的,”FiftyThree 联合创始人兼首席执行官 Georg Petschnigg 解释道。库贝尔卡-蒙克的论文使团队能够将美学问题转化为数学框架。 [...]

从所有这些信息来看,基于 Kubelka-Munk 模型的实施似乎可能是前进的方向,并提供更接近现实的结果。

尽管它看起来像一个复杂的过程,但我还没有看到太多关于如何实现这样的事情的好信息。


相关问题

这些问题是在此问题之后发布的,全部与同一件事有关。

他们都没有真正的答案。


其他相关链接和资源

The common mixing of RGB colors is very different from mixing colors for paintings,
it's mixing of light instead mixing of pigments.

For example:

Blue (0,0,255) + Yellow (255,255,0) = Grey (128,128,128)

(It should be Blue + Yellow = Green)

Is there any known algorithm for color mixing that works like mixing real colors?


My approach

I've already tried following:

Converting both colors to HSV and mixing hues (multiplied by coefficient computed from saturation),
and a simple average for saturation and value channels. Then I computed average luminance from both
colors and adjusted the resulting color to match this luminance.
This worked quite well, but the hue mixing was sometimes wrong, e. g.:

Red (Hue 0°) + Blue (Hue 240°) = Green (Hue 120°)

I've figured out that sometimes I need to shift the hue value by 360° (when the difference between
hues is greater than 180°).

Red (Hue 360°) + Blue (Hue 240°) = Magenta/fuchsia (Hue 300°)

But this shifting wasn't very good too, e.g.:

Cyan (Hue 179°) + Red (Hue 0°) = Hue 89.5°
Cyan (Hue 181°) + Red (Hue 0°) --> shifting is performed (the difference is greater than 180°)
Cyan (Hue 181°) + Red (Hue 360°) = Hue 270.5°

(Hue 179 + Red) and (Hue 181 + Red) results in two completely different colors.


Then I tried CIE Lab color space (as in Photoshop), which is designed to be closer to how humans perceive the colors.

I used just a simple average for each corresponding two channels, but the results weren't satisfying,
for example, I got pink (64, 26, -9.5) out of blue (98, -16, 93) and yellow (30, 68, -112). These coefficients
were taken from Photoshop.

Maybe if I used some different operation than average, it could work, but
I don't know what.


CMYK didn't work too, results are just like in RGB or LAB.


It seems that neither the trivial additive nor subtractive color mixing in any of these color spaces yields natural results.


Working implementations

Krita – Painterly mixer

Raster graphics editor Krita had a working implementation of more realistic color mixing at some point: http://commit-digest.org/issues/2007-08-12/ (Painterly mixer plugin)

They say it is the first public application that implements special technology using Kubelka and Munk equations that describe the behavior of pigments.

Here's a video of Krita color mixing: https://www.youtube.com/watch?v=lyLPZDVdQiQ

Paper by FiftyThree

There's also article about color blending in the Paper app for iOS developed by FiftyThree. They describe how they innovate and experiment in the area and also offer samples of mixing blue and yellow that results in green. However, the actual process or algorithm isn't really described there.

Quoting:

"In searching for a good blending algorithm, we initially tried interpolating across various color-spaces: RGB, HSV, and HSL, then CieLAB and CieLUV. The results were disappointing," says Chen. "We know red and yellow should yield orange, or that red and blue should make purple—but there isn't any way to arrive at these colors no matter what color-space you use. There's an engineering axiom: Do the simplest thing that could possibly work. Well, we had now tried the easiest possible approaches and they didn't feel even remotely right."

It seems that same as Krita, Paper implements the Kubelka-Munk model:

[...] the Kubelka-Munk model had at least six values for each color, including reflection and absorption values for each of the RGB colors. "While the appearance of a color on a screen can be described in three dimensions, the blending of color actually is happening in a six-dimensional space," explains Georg Petschnigg, FiftyThree's co-founder and CEO. The Kubelka-Munk paper had allowed the team to translate an aesthetic problem into a mathematical framework. [...]

From all this information, it seems that implementation based on the Kubelka-Munk model could be the way forward and offer results that are much closer to reality.

Even though it looks like a complicated process, I haven't yet seen much good information on how to implement something like this.


Related questions

These questions were posted after this one all relating to the same thing.

None of them really has the answer.


Other related links and resources

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

所谓喜欢 2024-08-10 03:08:58

正确的答案是否定的,因为没有正确工作模型来说明“现实世界中的颜色混合”如何真正发挥作用。它太复杂和有条件了,根本不像我们在学校学到的简单的红-蓝-黄的东西(实际上它需要所有的化学知识和大量的物理和生物学来解决)。

然而,简单化的答案是:是的,使用减法混合而不是加法混合。

我们在小学学到的颜色混合是基于颜料组合,这是减色混合的一种形式(非常简单)。也就是说,我们添加在一起的颜色越多,它就会变得越暗,因为每种颜料都会减去更多的光。

另一方面,几乎所有计算机配色方案都是相加的,因为它们基于组合光波(非常简单),因此它们变得更亮,因为每种颜色都会增加一点点光。

RGB+方案在某种程度上是我们在大多数美国小学中学到的减法方案(即RBY-)的加法补充。然而,它们并不完全匹配,并且它们之间的转换可能很困难(现在正在研究...)


好的,如果您只想从 RGB 的加法组合切换到减法组合,您可以使用以下反向贝叶斯类型组合两种颜色的公式:

NewColor.R = (Color1.R * Color2.R)/255
NewColor.G = (Color1.G * Color2.G)/255
NewColor.B = (Color1.B * Color2.B)/255

调整色极(G 到 Y,然后回到 G)的差异要困难得多...


有人指出,这会在示例问题中产生黑色,从技术上讲,这是正确的然而,对于真正的减色系统,如果您想要更多的稀释/减色系统,您可以尝试以下方法:

NewColor.R = 255 - SQRT(((255-Color1.R)^2 + (255-Color2.R)^2)/2)
NewColor.G = 255 - SQRT(((255-Color1.G)^2 + (255-Color2.G)^2)/2)
NewColor.B = 255 - SQRT(((255-Color1.B)^2 + (255-Color2.B)^2)/2)

这会产生深灰色而不是黑色。但要获得黄色或任何接近的颜色,您仍然必须解决颜色方案的极点对齐问题。

The correct answer is NO, because there is no correct working model of how "color mixing in the real world" really works. It is FAR too complex and conditional and not really at all like the simple Red-Blue-Yellow stuff that we learned in school (it in fact requires all of Chemistry and a lot of Physics and Biology to resolve).

However, the simplistic answer is: YES, use subtractive mixing rather than Additive mixing.

The color-mixing that we learned in grade school is based on pigment combinations which are a form of subtractive color mixing (very simplistically). That is the more colors that we add together, the darker it becomes because each pigment subtracts a little bit more light.

On the other hand, almost all computer color-schemes are additive in that they are based on combining light waves (very simplistically), so they get brighter, because each color adds a little bit more light.

The RGB+ scheme is somewhat, the additive complement to the subtractive scheme that we learned in most US elementary schools (which is RBY-). However, they do not match up exactly and it can be difficult to convert between them (researching now ...)


OK, if you just want to switch from additive combinations in RGB to subtractive ones, you can use the following reverse-bayesan type formula to combine two colors:

NewColor.R = (Color1.R * Color2.R)/255
NewColor.G = (Color1.G * Color2.G)/255
NewColor.B = (Color1.B * Color2.B)/255

Adjusting for the difference in the chromatic poles (G to Y, then back to G) is a lot harder ...


It has been pointed out that this produces Black for the example problem, and technically this is correct for a true subtractive system, however, if you want more diluting/subtractive system, you could try this instead:

NewColor.R = 255 - SQRT(((255-Color1.R)^2 + (255-Color2.R)^2)/2)
NewColor.G = 255 - SQRT(((255-Color1.G)^2 + (255-Color2.G)^2)/2)
NewColor.B = 255 - SQRT(((255-Color1.B)^2 + (255-Color2.B)^2)/2)

This produces a dark grey instead of Black. But to get Yellow or anything close, you still have to fix the color-scheme's pole-alignment problem.

書生途 2024-08-10 03:08:58

在 2 分钟论文中展示了一种新颖的技术(2022 年 2 月)

作者将其称为“Mixbox”,并且该技术是开源的(算法可以在 github 上找到,请注意虽然有许可证)。目前 Rebelle 5 正在实施这种混色技术,不久的将来可能会推出更多程序。

在我看来,它的最强点是它的实施简单性以及开箱即用的简单性。他们的论文(据说——我没有读过)也写得很好。

There is a novel (Feb 2022) technique showcased in 2 minute papers.

Autors call it the "Mixbox", and the technique is open source (algorithm can be found on github, mind the license though). Currently Rebelle 5 is implementing this color mixing technique, more programs may follow in the nearest future.

Imo the strongest point of this is its simplicity in implementation and how easily you can use it out of the box. Their paper is also (supposedly -- I didn't read it) well written.

一口甜 2024-08-10 03:08:58

组合颜色有两种不同的可能性:

  1. 加法混合(如 RGB)

  2. 减色混合(如 CMYK)

因此,在减色混合中,结果就是您所期望的,但没有蓝色,而是青色:

黄色 + 青色 = 绿色

一般来说,减色混合只是“拿走” “(过滤)来自白色,而加色混合则来自黑色。 (减色的基色与加色相反:红色 -> 青色;绿色 -> 洋红色;蓝色 -> 黄色)

因此,如果您从白色屏幕开始应用滤镜:

min( 白色 (255,255,255),黄色 (255,255,0 ), 青色 (0,255,255)) = 绿色 (0,255,0)

there are two different possibilities combining colors:

  1. additive mixing (like RGB)

  2. subtractive mixing (like CMYK)

So in subtractive color mixing the result is what you expected, but there is no blue, instead there is cyan:

Yellow + cyan = green

In general subtractive color mixing is just "taking away" (filtering) from white while additive color mixing is adding up from black. (base colors of subtractive are inverse from additive: red ->cyan; green->magenta; blue->yellow)

So if you start with white screen applying filters:

min( white (255,255,255), yellow (255,255,0), cyan (0,255,255)) = green (0,255,0)

划一舟意中人 2024-08-10 03:08:58

krita 中有以真实方式混合颜色的代码: https://projects.kde.org/projects/calligra/repository/revisions/master/show/krita/plugins/extensions/painterlyframework

请注意,包含光源文件的代码是 GPLv2+。它可以将 RGB 转换为波长,进行合成并转换回来。

There is code to mix colors in a realistic way in krita: https://projects.kde.org/projects/calligra/repository/revisions/master/show/krita/plugins/extensions/painterlyframework.

Note that the code including the illuminants file is GPLv2+. It can convert from RGB to wavelengths, do composition and convert back.

指尖上的星空 2024-08-10 03:08:58

我认为组合色调的问题在于,您是通过将两个角度相加并除以二来实现的。正如您所注意到的,结果通常毫无意义。我认为你最好将角度转换为单位圆上的笛卡尔坐标,对它们进行平均,然后找到结果点的角度(忽略大小)。

I think your problem with combining hues is that you are doing it by adding together the two angles and dividing by two. As you've noticed, the result often makes no sense. I think you'd be better off converting the angles to Cartesian coordinates on the unit circle, averaging those, and finding the angle of the resulting point (ignoring the magnitude).

╰◇生如夏花灿烂 2024-08-10 03:08:58

进行 RGB 颜色减色混合的一种方法是首先将 RGB 颜色转换为光谱反射率曲线。转换相当简单,完成后,您可以对反射率曲线进行真正的减法混合,然后将结果转换回 RGB。还有另一个类似的问题: stackoverflow.com/questions/10254022/,其中更详细地讨论了此过程。

One way to do subtractive mixture of RGB colors is to first convert the RGB colors to spectral reflectance curves. The conversion is fairly simple, and once you've done it, you can do a true subtractive mixture of the reflectance curves, and then convert the result back to RGB. There is another similar question: stackoverflow.com/questions/10254022/, where this process is discussed in more detail.

长伴 2024-08-10 03:08:58

想知道 RGB 值反转的计算是否有效。由于它是关于灯光的减法,从技术上讲,减法部分可以通过简单的数学来计算。

例如青色+黄色

青色=0x00ffff
黄色 = 0xffff00

它们的反转是 0xff0000 和 0x0000ff,这意味着它们完全吸收红光和蓝光。它们的 1:1 混合物应该吸收一半的红光和蓝光(因为混合物的另一半仍然可以反射一些红光和蓝光),这与 (0xff0000 + 0x00ffff) / 2 = 0x7f007f 一致。现在我们从 0xffffff 中减去该值,我们得到 0x80ff80,它是绿色的!

Wondering if calculation of inversion of the RGB value work. Since it's about subtraction of lights, technically the subtraction part can be calculated by simple math.

For example cyan + yellow

cyan = 0x00ffff
yellow = 0xffff00

Their inversions are 0xff0000 and 0x0000ff, meaning they absorbed red and blue lights completely. Their 1:1 mixture should absorb half of red and blue lights (since the other half of the mixture can still reflects some red and blue light), which is consistent with (0xff0000 + 0x00ffff) / 2 = 0x7f007f. Now we subtract the value from 0xffffff we have 0x80ff80 which is green!

Saygoodbye 2024-08-10 03:08:58

像颜料一样混合颜色

  1. 从所有颜色中去除白色,保留白色部分和彩色部分
  2. 对从颜色中去除的白色部分的 RGB 值进行
  3. 平均 对彩色部分的 RGB 值
  4. 进行平均从平均颜色部分中取出白色
  5. 白色值的一半删除并将该值添加到平均颜色部分的绿色中
  6. 添加平均白色部分并使其成为整数

Javascript 实现

// rgbColor is an array of colors,
// where each color is an array with the three color values (RGB, 0 to 255)
function mixRGB(rgbColors) {
        function sumColors(summedColors, nextColor) {
            return summedColors.map(
                (summedColor, i) => nextColor[i] + summedColor,
            );
        }

        function averageColors(colors) {
            return colors
                .reduce(sumColors, [0, 0, 0])
                .map(c => c/colors.length);
        }

        // Remove white from all colors
        const whiteParts = [];
        const colorParts = [];
        rgbColors.forEach(color => {
            const whiteVal = Math.min(...color);
            whiteParts.push([whiteVal, whiteVal, whiteVal]);
            colorParts.push(color.map(val => val - whiteVal));
        });
        
        // Average the whites from each selection
        const averagedWhite = averageColors(whiteParts);
        // Average all non-white colors
        let averagedColor = averageColors(colorParts);

        // Take out the white from the averaged colors
        const whitePart = Math.min(...averagedColor);
        averagedColor = averagedColor.map(color => color - whitePart);

        // Half the white value removed and add that value to the Green
        averagedColor[1] += (whitePart / 2);
        
        // Add the averaged white back in and make whole number
        averagedColor = averagedColor.map((color, i) => Math.floor(color + averagedWhite[i]));
    
        return averagedColor;
  }

我基于此构建了一个颜色游戏,你可以尝试一下

Mixing Colors like Pigments

  1. Remove white from all colors, keeping the white parts and color parts
  2. Average the RGB values of the white parts removed from the colors
  3. Average the RGB values of the color parts
  4. Take out the white from the averaged color parts
  5. Half the white value removed and add that value to the Green of the averaged color parts
  6. Add the averaged white parts back in and make whole number

Javascript Implementation

// rgbColor is an array of colors,
// where each color is an array with the three color values (RGB, 0 to 255)
function mixRGB(rgbColors) {
        function sumColors(summedColors, nextColor) {
            return summedColors.map(
                (summedColor, i) => nextColor[i] + summedColor,
            );
        }

        function averageColors(colors) {
            return colors
                .reduce(sumColors, [0, 0, 0])
                .map(c => c/colors.length);
        }

        // Remove white from all colors
        const whiteParts = [];
        const colorParts = [];
        rgbColors.forEach(color => {
            const whiteVal = Math.min(...color);
            whiteParts.push([whiteVal, whiteVal, whiteVal]);
            colorParts.push(color.map(val => val - whiteVal));
        });
        
        // Average the whites from each selection
        const averagedWhite = averageColors(whiteParts);
        // Average all non-white colors
        let averagedColor = averageColors(colorParts);

        // Take out the white from the averaged colors
        const whitePart = Math.min(...averagedColor);
        averagedColor = averagedColor.map(color => color - whitePart);

        // Half the white value removed and add that value to the Green
        averagedColor[1] += (whitePart / 2);
        
        // Add the averaged white back in and make whole number
        averagedColor = averagedColor.map((color, i) => Math.floor(color + averagedWhite[i]));
    
        return averagedColor;
  }

I built a color game based off this you can try

当爱已成负担 2024-08-10 03:08:58

检查此实现以了解加法、减法和其他混合算法。

功能齐全(用 java 编写),因此您可以测试需要混合的任何颜色,看看它是否适合您的需求。

正如其他回复所指出的,蓝色 + 黄色(确切地说青色 + 黄色)是绿色 > 关于减色 CMYK 算法。自己看吧

Check this implementation for additive, substractive and other mixing alghoritms.

Is fully functional (writed in java), so you can test whatever colors you need to mix, and see if it fit your needs.

As other responses pointed, Blue + Yellow (exactly Cyan + Yellow) is Green on substractive CMYK alghoritm. See by yourself

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文