2

There is a mismatch between light colors and paint: While a physicist will say that the three primary colors are red, green and blue, a painter will give red, blue and yellow as primary colors. Indeed, when painting with watercolors, you can't mix a yellow from red, green and blue, and instead of mixing orange you'll only get brown.

Here is what I'm trying to do: From two given RGB colors I'd like to calculate the RGB code for the combined color, and I'd like the colors to blend like watercolor would on paper. As I understood the calculation normally would look like this:

  • #FF0000 + #0000FF = #880088 ((FF+00)/2 = 88, (00+00)/2 = 00, (00+FF)/2 = 88), so red and blue gives purple (as it should)
  • #FF0000 + #FFFF00 = #FF8800 ((FF+FF)/2 = FF, (00+FF)/2 = 88, (00+00)/2 = 00), so red and yellow gives orange (as it should)

However, when mixing blue and yellow, the result is grey:

  • #0000FF + #FFFF00 = #888888 ((00+FF)/2 = 88, (00+FF)/2 = 88, (FF+00)/2 = 88) = GREY

while on paper you'd expect to get green (#008800) and could never get grey when mixing colors.

So my question is, how can I kind of exchange green with yellow as primary color, and how can I then calculate mixed colors which follow the laws of paint rather than those of light colors?

dankoliver
  • 732
  • 7
  • 13
  • 1
    Have you looked at: "[Algorithm for Additive Color Mixing for RGB Values](http://stackoverflow.com/questions/726549/algorithm-for-additive-color-mixing-for-rgb-values)" or "[Is there an algorithm for color mixing that works like mixing real colors?](http://stackoverflow.com/questions/1351442/is-there-an-algorithm-for-color-mixing-that-works-like-mixing-real-colors)"? – kmote Jan 26 '12 at 21:59

4 Answers4

5

There's no simple physical model which will do this, the painter's colors have very elaborate interactions with light. Fortunately we have computers, which are not limited to modelling the physical world - we can make them do any arbitrary thing we'd like!

The first step is to create a color wheel with the hue distribution that we require, with red, yellow, and blue at 120 degree increments. There are many examples on the web. I've created one here that only has fully saturated colors so that it can be used to generate the full RGB gamut. The colors on the wheel are completely arbitrary; I've set Orange (60°) to (255,160,0) because the midpoint between Red and Yellow was too red, and I've moved pure Blue (0,0,255) to 250° instead of 240° so that the 240° Blue would look better.

RYB hue color wheel

Remembering the experiments of my childhood, when you mix equal amounts of Red, Yellow, and Blue together you get an indistinct brownish gray. I've chosen a suitable color which you can see at the center of the color wheel; in the code I affectionately call it "mud".

To get every conceivable color you need more than Red, Yellow, and Blue, you also need to mix White and Black. For example you get Pink by mixing Red and White, and you get Brown by mixing Orange (Yellow+Red) with Black.

The conversion works with ratios, not absolute numbers. As with real paint there's no difference between mixing 1 part red and 1 part yellow, versus 100 parts red and 100 parts yellow.

The code is presented in Python but it shouldn't be hard to convert to other languages. The trickiest part is adding the Red, Yellow and Blue to create a hue angle. I use vector addition and convert back to an angle with atan2. Almost everything else is done with linear interpolation (lerp).

# elementary_colors.py
from math import degrees, radians, atan2, sin, cos

red = (255, 0, 0)
orange = (255, 160, 0)
yellow = (255, 255, 0)
green = (0, 255, 0)
cyan = (0, 255, 255)
blue = (0, 0, 255)
magenta = (255, 0, 255)
white = (255, 255, 255)
black = (0, 0, 0)
mud = (94, 81, 74)

colorwheel = [(0, red), (60, orange), (120, yellow), (180, green),
              (215, cyan), (250, blue), (330, magenta), (360, red)]

red_x, red_y = cos(radians(0)), sin(radians(0))
yellow_x, yellow_y = cos(radians(120)), sin(radians(120))
blue_x, blue_y = cos(radians(240)), sin(radians(240))

def lerp(left, right, left_part, total):
    if total == 0:
        return left
    ratio = float(left_part) / total
    return [l * ratio + r * (1.0 - ratio) for l,r in zip(left, right)]

def hue_to_rgb(deg):
    deg = deg % 360
    previous_angle, previous_color = colorwheel[0]
    for angle, color in colorwheel:
        if deg <= angle:
            return lerp(previous_color, color, angle - deg, angle - previous_angle)
        previous_angle = angle
        previous_color = color

def int_rgb(rgb):
    return tuple(int(c * 255.99 / 255) for c in rgb)

def rybwk_to_rgb(r, y, b, w, k):
    if r == 0 and y == 0 and b == 0:
        rgb = white
    else:
        hue = degrees(atan2(r * red_y + y * yellow_y + b * blue_y,
                            r * red_x + y * yellow_x + b * blue_x))
        rgb = hue_to_rgb(hue)
        rgb = lerp(mud, rgb, min(r, y, b), max(r, y, b))
    gray = lerp(white, black, w, w+k)
    rgb = lerp(rgb, gray, r+y+b, r+y+b+w+k)
    return int_rgb(rgb)
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
1

I suggest backing away from the notion of primaries, whether RGB or RYB or CMY, etc.

As I understand it, your goal is to mix colors (that are described in RGB color space) in a way that simulates how paint will mix, i.e., mix subtractively. Converting from RGB to CMY or CMYK won't be productive, because you still have to deal with the subtractive mixture calculation, which isn't any easier to do in CMY space.

Instead, let me suggest that you think about converting 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: https://stackoverflow.com/questions/10254022/, where this process is discussed in more detail.

Community
  • 1
  • 1
Scott Burns
  • 81
  • 1
  • 4
1

If you mix red, blue and yellow paints together, you get a murky brown, not black. That's because "red, blue and yellow" are not really the exact primary colours there. Cyan, Magenta and Yellow are, which is why printers work in CMYK (K being black).

So what you're actually asking is how to convert an RGB colour to CMYK, in which case any of these links would help you.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • RGB are considered the primary colors of light, which are http://en.wikipedia.org/wiki/Additive_color. CMYK are considered the primary colors of paint, which are http://en.wikipedia.org/wiki/Subtractive_color. Understanding the difference will help you understand why and when you need to convert between models. – dkamins Jan 26 '12 at 22:14
  • Dye colors generally obey subtractive color rules (so e.g. mixing magenta with cyan will yield blue, and mixing blue with yellow will yield something pretty dark) but paint colors don't come close. Mix yellow paint with a non-greenish blue (not cyan), and the result will generally be green. Even dye colors' adherence to the subtractive color rules may be dubious except when dies are chosen for their ability to interact predictably. – supercat Jun 11 '12 at 22:11
0

Cyan, Magenta and Yellow is what it should be Red, Yellow and Blue is a medieval convention.

Apparently it's not an easy switch, and I fell asleep in the middle of the math, but here's one way.

RYB RGB Conversion

Tony Hopkinson
  • 20,172
  • 3
  • 31
  • 39