Имеем 2 цвета, заданы в RGB, по 1 байту на цвет.
Если брать и складывать каждый из цветов, затем делить на 2, то получается на мой
взгляд не совсем верно. Допустим FF0000 и 00FF00 таким образом родят 7F7F00, который
смотрится намного темнее, чем оба его "родителя". Если таким образом считать средний
между FF0000 и FFFF00, то будет FF7F00, который на взгляд явно ближе к FF0000, чем
к FFFF00. На взгляд средний между ними примерно FFAA00.
В каждом пикселе монитора мы имеем 3 субпикселя, питание подаваемое на которые изменяется
от 0 до 255 (некоторых баллов) при данной глубине цвета, если я всё верно понимаю.
Хочу узнать, как можно математически просчитать цвет, который был бы "средним" между
двумя заданными, а именно был бы похож на каждый из них в равной мере, при этом имея
среднюю между ними яркость.
Возможно, есть стандартные библиотеки в средах разработки, которые делают что-то
подобное?
div {
width: 6em;
height: 6em;
display: inline-block;
font-family: monospace;
text-indent: -12ch;
overflow: hidden;
}
div:after {
content: attr(style);
}
Ответы
Ответ 1
В RGB нельзя высчитать "средний" цвет. Во первых, яркость разных цветов воспринимается
глазом по разному - т.е. яркость не "определяется наибольшим значением любого из 3
цветов".
В RGB (sRGB) зеленый ярче красного, красный ярче синего. FF0000 по яркости совпадает
с 009B00 (справа), а не с вырвиглазным 00FF00 (слева):
Во вторых, отсчет яркости не привязан строго к 0, и простое "уполовинивание" значения
дает слишком темные цвета. Например, "полуяркий красный" - это, скорее, A60000, а не
7F0000, как можно было бы ожидать.
Для правильного смешивания цветов их стоит предварительно перевести в цветовое пространство,
отражающее человеческое восприятие, например, Lab color space, и высчитывать среднее
уже в этом пространстве. Вот сравнительные результаты для чистых R G и B (сверху -
с преобразованием в Lab и усреднением, снизу - с усреднением в RBG).
Видно, что усреднение по RGB дает слишком темный результат, а усреднение через Lab
- средний по яркости.
В C# реализуется достаточно легко, готовые форумы есть по ссылкам на вики выше. Если
не хочется вручную вписывать преобразование - возьмите готовую библиотеку:
PM> Install-Package ColorMine
var color1 = new Rgb() { R = 255 };
var color1Lab = color1.To();
var color2 = new Rgb() { G = 255 };
var color2Lab = color2.To();
var averageLab = new Lab()
{
L = (color1Lab.L + color2Lab.L) / 2,
A = (color1Lab.A + color2Lab.A) / 2,
B = (color1Lab.B + color2Lab.B) / 2,
};
var averageLabRgb = averageLab.ToRgb();
Ответ 2
Есть две модели смешивания: адитивное и субтрактивное смешение цветов. Можно почитать
тут https://creativshik.com/pochemu-vret-monitor/
Адитивное будет так (оно "работает" при световом смешивании лучей, в некоторых графических
программах):
r = (r1 + r2 ) / 2;
g = (b1 + b2 ) / 2;
b = (b1 + b2 ) / 2;
Cубтрактивное
r = // TODO:
g =
b =
Второе "смешивание" цветов похоже на привычное смешивание "акварельных красок" на
белом листе. Ещё такое смешивание можно включить в Corel Draw.
Адитивное - "приближает" цвета к чёрному. А субтрактивное - к белому.
Если стоит задача определить "ближайший" цвет,
то используется сумма rgb c коефициентами
Q = r * Kr + g* Kg + b*Kb;
Где Q- будет "вес" по которому можно определить схожесть. этой формулой переводят
изображение в чёрнобелое. Для разных преобразований используют разные коефициенты.
Стандартные для ч/б где-то видел - найду запишу.
Коефициенты нашёл, вот
Y=0.299*R+0.587*G+0.114*B (В.Порев "Компьютерная графика", BHV, 2002г. -
страница 41.)
Y= 0.2125R + 0.7154G + 0,0721В (Фотошоп ссылка )
При попытке вставить коефициенты - они в формуле сокращаются, т.е. для Y(R=255,G=0,B=0)=76,5
, Y(R=0;G=255;B=0)=150,45. Средний цвет будет (128,128,0), но и средняя яркость будет
тоже средней - 113,48. Если вы хотите "поднять" яркость до max(Y(цвет1),Y(цвет2)) то
вы можете пересчитать цвет, учитывая формулу приведённую выше.
Если у вас задача "не потерять яркость", тогда ставим задачу - сохранить максимальную
яркость.
Y(Red=255) = 76,5 Y(Green=255) = 150,45, Y=(Red+Green) = 113,48;
t = Ymax / Y = 150,45/113,48 = 1,325
Тогда (адитивное смешение):
r` = (r1 + r2)*t/2 = (255 + 0)*1,325/2 = 170
g` = (g1 + g2)*t/2 = (0 + 255)*1,325/2 = 170
b` = (b1 + b2)*t/2 = (0 + 0)*1,325 / 2 = 0
Y(R=170,G=170,B=0) = 150,45
Для субтрактивного смешения можно написать аналогичную формулу. Можно так же в цикле
прибавлять еденицу - выйдет похожее значение (R=169,G=127,B=42). Внизу пример, сначала
R (255,0,0), потом результат "затемнённый" R+G (128,128,0), Потом скорректированый
по яркости (170,170,0), потом сделаный инкрементом (169,127,42). Потом зеленый (0,255,0)
И последнее, ставьте проверки что б компонента не выходила за пределы [0,255], если
меньше 0 то присваивать 0, если более 255 то ставить 255.