Алгоритм аддитивного смешения цветов для значений RGB - PullRequest
61 голосов
/ 07 апреля 2009

Я ищу алгоритм для смешивания цветов для значений RGB.

Это так же просто, как сложить значения RGB вместе максимум до 256?

(r1, g1, b1) + (r2, g2, b2) =
    (min(r1+r2, 256), min(g1+g2, 256), min(b1+b2, 256))  

Ответы [ 10 ]

77 голосов
/ 07 апреля 2009

Для смешивания с использованием альфа-каналов вы можете использовать следующие формулы:

r = new Color();
r.A = 1 - (1 - fg.A) * (1 - bg.A);
if (r.A < 1.0e-6) return r; // Fully transparent -- R,G,B not important
r.R = fg.R * fg.A / r.A + bg.R * bg.A * (1 - fg.A) / r.A;
r.G = fg.G * fg.A / r.A + bg.G * bg.A * (1 - fg.A) / r.A;
r.B = fg.B * fg.A / r.A + bg.B * bg.A * (1 - fg.A) / r.A;

fg - цвет краски. bg это фон. r - результирующий цвет. 1.0e-6 - просто очень маленькое число, чтобы компенсировать ошибки округления.

ПРИМЕЧАНИЕ: Все используемые здесь переменные находятся в диапазоне [0.0, 1.0]. Вы должны разделить или умножить на 255, если хотите использовать значения в диапазоне [0, 255].

Например, 50% красного поверх 50% зеленого:

// background, 50% green
var bg = new Color { R = 0.00, G = 1.00, B = 0.00, A = 0.50 };
// paint, 50% red
var fg = new Color { R = 1.00, G = 0.00, B = 0.00, A = 0.50 };
// The result
var r = new Color();
r.A = 1 - (1 - fg.A) * (1 - bg.A); // 0.75
r.R = fg.R * fg.A / r.A + bg.R * bg.A * (1 - fg.A) / r.A; // 0.67
r.G = fg.G * fg.A / r.A + bg.G * bg.A * (1 - fg.A) / r.A; // 0.33
r.B = fg.B * fg.A / r.A + bg.B * bg.A * (1 - fg.A) / r.A; // 0.00

Результирующий цвет: (0.67, 0.33, 0.00, 0.75), или 75% коричневый (или темно-оранжевый).


Вы также можете изменить эти формулы:

var bg = new Color();
if (1 - fg.A <= 1.0e-6) return null; // No result -- 'fg' is fully opaque
if (r.A - fg.A < -1.0e-6) return null; // No result -- 'fg' can't make the result more transparent
if (r.A - fg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important
bg.A = 1 - (1 - r.A) / (1 - fg.A);
bg.R = (r.R * r.A - fg.R * fg.A) / (bg.A * (1 - fg.A));
bg.G = (r.G * r.A - fg.G * fg.A) / (bg.A * (1 - fg.A));
bg.B = (r.B * r.A - fg.B * fg.A) / (bg.A * (1 - fg.A));

или

var fg = new Color();
if (1 - bg.A <= 1.0e-6) return null; // No result -- 'bg' is fully opaque
if (r.A - bg.A < -1.0e-6) return null; // No result -- 'bg' can't make the result more transparent
if (r.A - bg.A < 1.0e-6) return bg; // Fully transparent -- R,G,B not important
fg.A = 1 - (1 - r.A) / (1 - bg.A);
fg.R = (r.R * r.A - bg.R * bg.A * (1 - fg.A)) / fg.A;
fg.G = (r.G * r.A - bg.G * bg.A * (1 - fg.A)) / fg.A;
fg.B = (r.B * r.A - bg.B * bg.A * (1 - fg.A)) / fg.A;

Формулы рассчитают, что цвет фона или краски должен быть для получения заданного результирующего цвета.


Если ваш фон непрозрачный, результат также будет непрозрачным. Цвет переднего плана может принимать диапазон значений с различными значениями альфа. Для каждого канала (красного, зеленого и синего) необходимо проверить, какой диапазон альфа-каналов приводит к допустимым значениям (0 - 1).

55 голосов
/ 07 апреля 2009

Это зависит от того, что вы хотите, и это может помочь увидеть, каковы результаты различных методов.

Если хочешь

Red + Black        = Red
Red + Green        = Yellow
Red + Green + Blue = White
Red + White        = White 
Black + White      = White

, затем добавление с помощью зажима работает (например, min(r1 + r2, 255)). Это больше похоже на модель освещения, на которую вы ссылались.

Если хочешь

Red + Black        = Dark Red
Red + Green        = Dark Yellow
Red + Green + Blue = Dark Gray
Red + White        = Pink
Black + White      = Gray

тогда вам нужно будет усреднить значения (например, (r1 + r2) / 2). Это работает лучше для осветления / затемнения цветов и создания градиентов.

46 голосов
/ 28 марта 2015

Забавный факт: компьютерные значения RGB получены из квадратного корня потока фотонов. Так что, как общая функция, ваша математика должна принимать это во внимание. Общая функция для данного канала:

blendColorValue(a, b, t)
    return sqrt((1 - t) * a^2 + t * b^2)

Где a и b - это цвета для смешивания, а t - это число от 0 до 1, представляющее нужную точку в смешивании между a и b.

Альфа-канал отличается; он не отражает интенсивность фотонов, а только процент фона, который должен просвечивать; поэтому при смешивании альфа-значений достаточно линейного среднего:

blendAlphaValue(a, b, t)
    return (1-t)*a + t*b;

Итак, для обработки смешивания двух цветов с использованием этих двух функций вам пригодится следующий псевдокод:

blendColors(c1, c2, t)
    ret
    [r, g, b].each n ->
        ret[n] = blendColorValue(c1[n], c2[n], t)
    ret.alpha = blendAlphaValue(c1.alpha, c2.alpha, t)
    return ret

Между прочим, я жажду языка программирования и клавиатуры, которые бы оба позволили чисто математически (или более) представлять математику (объединяющий символ Unicode с надписью не работает для верхних индексов, символов и большого количества других символов) и интерпретировать его правильно. sqrt ((1-t) * pow (a, 2) + t * pow (b, 2)) просто не читается как чистый.

8 голосов
/ 07 апреля 2009

Несколько баллов:

  • Я думаю, что вы хотите использовать min вместо max
  • Я думаю, что вы хотите использовать 255 вместо 256

Это даст:

(r1, g1, b1) + (r2, g2, b2) = (мин (r1 + r2, 255), мин (g1 + g2, 255), мин (b1 + b2, 255))

Однако, «естественным» способом смешивания цветов является использование среднего, и тогда вам не нужно минимальное значение:

(r1, g1, b1) + (r2, g2, b2) = ((r1 + r2) / 2, (g1 + g2) / 2, (b1 + b2) / 2)

6 голосов
/ 18 июля 2012

Javascript функция для смешивания цветов RGBA

c1, c2 и результат - как в JSON c1 = {r: 0,5, г: 1, b: 0, a: 0,33}

    var rgbaSum = function(c1, c2){
       var a = c1.a + c2.a*(1-c1.a);
       return {
         r: (c1.r * c1.a  + c2.r * c2.a * (1 - c1.a)) / a,
         g: (c1.g * c1.a  + c2.g * c2.a * (1 - c1.a)) / a,
         b: (c1.b * c1.a  + c2.b * c2.a * (1 - c1.a)) / a,
         a: a
       }
     } 
4 голосов
/ 06 мая 2015

ПИТОН ЦВЕТ СМЕШИВАНИЕ ЧЕРЕЗ ДОПОЛНЕНИЕ IN CMYK ПРОБЕЛ

Один из возможных способов сделать это - сначала преобразовать цвета в формат CMYK , добавить их туда, а затем снова преобразовать в RGB.

Вот пример кода на Python:

rgb_scale = 255
cmyk_scale = 100


def rgb_to_cmyk(self,r,g,b):
    if (r == 0) and (g == 0) and (b == 0):
        # black
        return 0, 0, 0, cmyk_scale

    # rgb [0,255] -> cmy [0,1]
    c = 1 - r / float(rgb_scale)
    m = 1 - g / float(rgb_scale)
    y = 1 - b / float(rgb_scale)

    # extract out k [0,1]
    min_cmy = min(c, m, y)
    c = (c - min_cmy) 
    m = (m - min_cmy) 
    y = (y - min_cmy) 
    k = min_cmy

    # rescale to the range [0,cmyk_scale]
    return c*cmyk_scale, m*cmyk_scale, y*cmyk_scale, k*cmyk_scale

def cmyk_to_rgb(self,c,m,y,k):
    """
    """
    r = rgb_scale*(1.0-(c+k)/float(cmyk_scale))
    g = rgb_scale*(1.0-(m+k)/float(cmyk_scale))
    b = rgb_scale*(1.0-(y+k)/float(cmyk_scale))
    return r,g,b

def ink_add_for_rgb(self,list_of_colours):
    """input: list of rgb, opacity (r,g,b,o) colours to be added, o acts as weights.
    output (r,g,b)
    """
    C = 0
    M = 0
    Y = 0
    K = 0

    for (r,g,b,o) in list_of_colours:
        c,m,y,k = rgb_to_cmyk(r, g, b)
        C+= o*c
        M+=o*m
        Y+=o*y 
        K+=o*k 

    return cmyk_to_rgb(C, M, Y, K)

Результат вашего вопроса будет (при условии, что половина ваших двух цветов смешана наполовину:

r_mix, g_mix, b_mix = ink_add_for_rgb([(r1,g1,b1,0.5),(r2,g2,b2,0.5)])

где 0,5, чтобы сказать, что мы смешиваем 50% первого цвета с 50% второго цвета.

3 голосов
/ 07 апреля 2009

Да, это так просто. Другой вариант - найти среднее (для создания градиентов).

Это действительно зависит от того, какого эффекта вы хотите достичь.

Однако, когда Альфа добавляется, это становится сложным. Существует несколько различных способов смешивания с использованием альфа.

Пример простого альфа-смешивания: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending

1 голос
/ 22 ноября 2018

Когда я пришел сюда, я не нашел алгоритм «аддитивного смешения цветов», который на самом деле искал, который также доступен в Photoshop и описан как «Экран» в Википедия . (Также известный как «увеличить яркость» или «инвертировать умножение».) Результат дает результат, аналогичный двум объединенным источникам света.

В режиме смешивания экрана значения пикселей в двух слоях инвертируются, умножаются, а затем снова инвертируются. Это дает противоположный эффект для умножения. В результате получается более яркая картина.

Вот оно:

// (rgb values are 0-255)
function screen(color1, color2) {
    var r = Math.round((1 - (1 - color1.R / 255) * (1 - color2.R / 255)) * 255);
    var g = Math.round((1 - (1 - color1.G / 255) * (1 - color2.G / 255)) * 255);
    var b = Math.round((1 - (1 - color1.B / 255) * (1 - color2.B / 255)) * 255);
    return new Color(r, g, b);
}
1 голос
/ 14 августа 2018

Написал / использовал что-то вроде @ Markus Jarderot sRGB смешивающий ответ (который не корректируется гаммой, поскольку это наследие по умолчанию) с использованием C ++

//same as Markus Jarderot's answer
float red, green, blue;
alpha = (1.0 - (1.0 - back.alpha)*(1.0 - front.alpha));
red   = (front.red   * front.alpha / alpha + back.red   * back.alpha * (1.0 - front.alpha));
green = (front.green * front.alpha / alpha + back.green * back.alpha * (1.0 - front.alpha));
blue  = (front.blue  * front.alpha / alpha + back.blue  * back.alpha * (1.0 - front.alpha));

//faster but equal output
alpha = (1.0 - (1.0 - back.alpha)*(1.0 - front.alpha));
red   = (back.red   * (1.0 - front.alpha) + front.red   * front.alpha);
green = (back.green * (1.0 - front.alpha) + front.green * front.alpha);
blue  = (back.blue  * (1.0 - front.alpha) + front.blue  * front.alpha);

//even faster but only works when all values are in range 0 to 255
int red, green, blue;
alpha = (255 - (255 - back.alpha)*(255 - front.alpha));
red   = (back.red   * (255 - front.alpha) + front.red   * front.alpha) / 255;
green = (back.green * (255 - front.alpha) + front.green * front.alpha) / 255;
blue  = (back.blue  * (255 - front.alpha) + front.blue  * front.alpha) / 255;

больше информации: "что каждый кодер должен знать о гамме"

0 голосов
/ 30 января 2016

Вот высокооптимизированный, автономный общедоступный класс c ++ с плавающей запятой и двумя по-разному оптимизированными 8-битными механизмами смешивания как в функциональном, так и в макроформатах, а также техническое обсуждение как рассматриваемой проблемы, так и того, как и важность, оптимизация этого вопроса:

https://github.com/fyngyrz/colorblending

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...