Формула для определения яркости цвета RGB

387

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

robmerica
источник
8
Воспринимаемая яркость - это то, что, я думаю, я ищу, спасибо.
robmerica
2
Есть хорошая статья ( Управление цветами в .NET - часть 1 ) о цветовых пространствах и разговорах между ними, включая теорию и код (C #). Для ответа смотрите в разделе « Преобразование между моделями» в статье.
подчеркиваем
4
Я был членом в течение многих лет, и я никогда не делал этого раньше. Могу ли я предложить вам просмотреть ответы и переосмыслить, какой из них принять?
Джайв

Ответы:

456

Вы имеете в виду яркость? Воспринимаемая яркость? Светимость?

  • Яркость (стандарт для определенных цветовых пространств): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Яркость (воспринимается вариант 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Яркость (вариант 2 воспринимается медленнее для расчета): sqrt( 0.241*R^2 + 0.691*G^2 + 0.068*B^2 )sqrt( 0.299*R^2 + 0.587*G^2 + 0.114*B^2 )(благодаря @MatthewHerbst ) [3]
анонимное
источник
26
Обратите внимание, что оба они подчеркивают физиологические аспекты: глазное яблоко человека наиболее чувствительно к зеленому свету, меньше к красному и меньше к синему.
Боб Кросс
16
Также обратите внимание, что все это, вероятно, для линейного 0-1 RGB, и у вас, вероятно, есть гамма-коррекция 0-255 RGB. Они не обращены так, как вы думаете.
Алекс странно
4
Неправильно. Перед применением линейного преобразования необходимо сначала применить обратную гамма-функцию для цветового пространства. Затем после применения линейной функции применяется гамма-функция.
Джайв Дадсон
6
В последней формуле это (0,299 * R) ^ 2 или 0,299 * (R ^ 2)?
Кайзер Созай
3
@KaizerSozay Как написано здесь, это будет означать 0.299*(R^2)(потому что возведение в степень идет до умножения)
Dantevg
298

Я думаю, что вы ищете формулу преобразования RGB -> Luma .

Фотометрический / цифровой ITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

Цифровой ITU BT.601 (придает больший вес компонентам R и B):

Y = 0.299 R + 0.587 G + 0.114 B

Если вы хотите обменять точность на производительность, для этого есть две формулы аппроксимации:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Их можно быстро рассчитать как

Y = (R+R+B+G+G+G)/6

Y = (R+R+R+B+G+G+G+G)>>3
Франси Пенов
источник
47
Мне нравится, что вы вводите точные значения, но также включаете быстрый «достаточно близкий» тип ярлыка. +1.
Беска
3
@Jonathan Dumaine - две формулы для быстрого расчета включают в себя синий - 1-й (2 * Красный + Blue+ 3 * Зеленый) / 6, 2-й (3 * Красный + Blue+ 4 * Зеленый) >> 3. Разумеется, в обоих быстрых приближениях синий имеет наименьший вес, но он все еще там.
Франси Пенов
84
@JonathanDumaine Это потому, что человеческий глаз наименее восприимчив к
голубому
4
Быстрая версия работает хорошо. Протестировано и применено к реальному приложению с тысячами пользователей, все выглядит отлично.
milosmns
10
Быстрая версия будет еще быстрее, если вы сделаете это как: Y = (R<<1+R+G<<2+B)>>3(это всего лишь 3-4 такта процессора в ARM), но я думаю, хороший компилятор сделает эту оптимизацию за вас.
rjmunro
105

Я сделал сравнение трех алгоритмов в принятом ответе. Я генерировал цвета в цикле, где использовался только каждый 400-й цвет. Каждый цвет представлен 2x2 пикселя, цвета сортируются от самых темных к самым светлым (слева направо, сверху вниз).

1-е изображение - Яркость (относительная)

0.2126 * R + 0.7152 * G + 0.0722 * B

2-я картинка - http://www.w3.org/TR/AERT#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

3-я картина - HSP Color Model

sqrt(0.299 * R^2 + 0.587 * G^2 + 0.114 * B^2)

4-е изображение - формула относительной яркости и контрастности WCAG 2.0 SC 1.4.3 (см . Ответ @ Synchro здесь )

На 1-м и 2-м снимках иногда можно увидеть рисунок в зависимости от количества цветов в одном ряду. Я никогда не замечал никаких паттернов на картинке из 3-го или 4-го алгоритма

Если бы мне пришлось выбирать, я бы пошел с алгоритмом № 3, так как его гораздо проще реализовать и он примерно на 33% быстрее, чем 4-й.

Воспринимаемый алгоритм сравнения яркости

Петр Хуртак
источник
3
Для меня это лучший ответ, потому что вы используете шаблон изображения, который позволяет вам воспринимать различные оттенки с одинаковой яркостью. Для меня и моего текущего монитора 3-я картинка «самая
привлекательная
8
Ваше изображение сравнения неверно, потому что вы не предоставили правильный ввод для всех функций. Первая функция требует линейного входа RGB; Я могу воспроизвести эффект полосатости только с помощью нелинейного (т.е. с гамма-коррекцией) RGB. Исправляя эту проблему, вы не получаете артефактов полосатости, и первая функция - явный победитель.
Макс
1
@Max ^2и, sqrtвключенные в третью формулу, являются более быстрым способом приближения линейного RGB от нелинейного RGB вместо, ^2.2и ^(1/2.2)это было бы более правильным. Использование нелинейных входов вместо линейных, к сожалению, чрезвычайно распространено.
Марк Рэнсом
53

Ниже приведен единственный ПРАВИЛЬНЫЙ алгоритм для преобразования изображений sRGB, используемых в браузерах и т. Д., В оттенки серого.

Необходимо применить обратную гамма-функцию для цветового пространства, прежде чем вычислять внутреннее произведение. Затем вы применяете гамма-функцию к уменьшенному значению. Невыполнение гамма-функции может привести к ошибкам до 20%.

Для типичных компьютерных вещей цветовое пространство sRGB. Правильные цифры для sRGB - ок. 0,21, 0,72, 0,07. Гамма для sRGB является сложной функцией, которая приближает возведение в степень на 1 / (2.2). Здесь все это в C ++.

// sRGB luminance(Y) values
const double rY = 0.212655;
const double gY = 0.715158;
const double bY = 0.072187;

// Inverse of sRGB "gamma" function. (approx 2.2)
double inv_gam_sRGB(int ic) {
    double c = ic/255.0;
    if ( c <= 0.04045 )
        return c/12.92;
    else 
        return pow(((c+0.055)/(1.055)),2.4);
}

// sRGB "gamma" function (approx 2.2)
int gam_sRGB(double v) {
    if(v<=0.0031308)
        v *= 12.92;
    else 
        v = 1.055*pow(v,1.0/2.4)-0.055;
    return int(v*255+0.5); // This is correct in C++. Other languages may not
                           // require +0.5
}

// GRAY VALUE ("brightness")
int gray(int r, int g, int b) {
    return gam_sRGB(
            rY*inv_gam_sRGB(r) +
            gY*inv_gam_sRGB(g) +
            bY*inv_gam_sRGB(b)
    );
}
Джайв Дадсон
источник
5
Именно так определяется sRGB. Я думаю, что причина в том, что он избегает некоторых численных проблем вблизи нуля. Это не будет иметь большого значения, если вы просто увеличите числа до степеней 2.2 и 1 / 2.2.
Джайв Дадсон
8
JMD - как часть работы в лаборатории визуального восприятия, я провел прямые измерения яркости на ЭЛТ-мониторах и могу подтвердить, что в нижней части диапазона значений есть линейная область яркости.
Джерри Федерспил
2
Я знаю, что это очень старый, но его все еще нужно искать. Я не думаю, что это может быть правильно. Не должен ли серый (255,255,255) = серый (255,0,0) + серый (0,255,0) + серый (0,0,255)? Это не так.
DCBillen
2
@DCBillen: нет, поскольку значения находятся в нелинейном гамма-исправленном пространстве sRGB, вы не можете просто сложить их. Если вы хотите добавить их, вы должны сделать это перед вызовом gam_sRGB.
февраля
1
@DCBillen Rdb правильно. Способ их сложения показан в функции int grey (int r, int g, int b), которая «вызывает» gam_sRGB. Мне больно, что через четыре года правильный ответ оценивается так низко. :-) Не совсем .. Я с этим справлюсь.
Джайв Дадсон
45

«Принятый» ответ является неправильным и неполным

Единственные точные ответы - это @ jive-dadson и @EddingtonsMonkey , а также support @ nils-pipenbrinck . Другие ответы (в том числе принятые) связаны или ссылаются на источники, которые являются либо неправильными, нерелевантными, устаревшими или сломанными.

Кратко:

  • Перед применением коэффициентов sRGB должен быть ЛИНЕРИРОВАННЫМ .
  • Яркость (L или Y) линейна, как и свет.
  • Воспринимаемая легкость (L *) нелинейна, как и восприятие человека.
  • HSV и HSL даже отдаленно не точны с точки зрения восприятия.
  • Стандарт МЭК для sRGB определяет пороговое значение 0,04045, а НЕ 0,03928 (это было из устаревшей ранней версии).
  • Полезные (то есть относительно восприятия) евклидовы расстояния требуют перцептуально однородного декартова векторного пространства, такого как CIELAB. sRGB не один.

Далее следует правильный и полный ответ:

Поскольку эта тема высоко ценится в поисковых системах, я добавляю этот ответ, чтобы прояснить различные неправильные представления по этому вопросу.

Яркость - это атрибут восприятия, он не имеет прямого измерения.

Воспринимаемая легкость измеряется некоторыми моделями зрения, такими как CIELAB, здесь L * (Lstar) является мерой воспринимаемой легкости и нелинейна для аппроксимации кривой нелинейного отклика человеческого зрения.

Яркость - это линейная мера света, спектрально взвешенная для нормального зрения, но не отрегулированная для нелинейного восприятия яркости.

Luma ( prime) - это гамма-кодированный, взвешенный сигнал, используемый в некоторых кодировках видео. Это не следует путать с линейной яркостью.

Гамма- кривая или кривая передачи (TRC) - это кривая, которая часто похожа на кривую восприятия и обычно применяется к данным изображения для хранения или широковещательной передачи, чтобы уменьшить воспринимаемый шум и / или улучшить использование данных (и связанные с этим причины).

Чтобы определить воспринимаемую яркость , сначала преобразуйте значения изображения R´G´B´ в гамма-коде в линейную яркость ( Lили Y), а затем в нелинейную воспринимаемую яркость ( L*)


НАЙТИ ЛЮМИНАНС:

... Потому что, видимо, это было где-то потеряно ...

Шаг первый:

Преобразовать все 8-битные целочисленные значения sRGB в десятичные 0.0-1.0

  vR = sR / 255;
  vG = sG / 255;
  vB = sB / 255;

Шаг второй:

Преобразовать гамма-кодированный RGB в линейное значение. Например, sRGB (компьютерный стандарт) требует кривой мощности приблизительно V ^ 2.2, хотя «точное» преобразование:

sRGB в линейный

Где V´ - канал R, G или B с гамма-кодированием sRGB.
псевдокод:

function sRGBtoLin(colorChannel) {
        // Send this function a decimal sRGB gamma encoded color value
        // between 0.0 and 1.0, and it returns a linearized value.

    if ( colorChannel <= 0.04045 ) {
            return colorChannel / 12.92;
        } else {
            return pow((( colorChannel + 0.055)/1.055),2.4));
        }
    }

Шаг третий:

Чтобы найти яркость (Y), примените стандартные коэффициенты для sRGB:

Применить коэффициенты Y = R * 0,2126 + G * 0,7152 + B * 0,0722

Псевдокод с использованием вышеуказанных функций:

Y = (0.2126 * sRGBtoLin(vR) + 0.7152 * sRGBtoLin(vG) + 0.0722 * sRGBtoLin(vB))

НАЙТИ ПЕРСПЕКТИВНОЕ МОЛНИЕ:

Шаг четвертый:

Возьмите яркость Y сверху и преобразуйте в L *

L * из уравнения Y
псевдокод:

function YtoLstar(Y) {
        // Send this function a luminance value between 0.0 and 1.0,
        // and it returns L* which is "perceptual lightness"

    if ( Y <= (216/24389) {       // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036
            return Y * (24389/27);  // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296
        } else {
            return pow(Y,(1/3)) * 116 - 16;
        }
    }

L * - это значение от 0 (черный) до 100 (белый), где 50 - перцептуальный «средний серый». L * = 50 является эквивалентом Y = 18,4 или, другими словами, 18% серая карта, представляющая середину фотографической экспозиции (зона V Анселя Адамса).

Ссылки:

IEC 61966-2-1:1999 Standard
Wikipedia sRGB
Wikipedia CIELAB
Wikipedia CIEXYZ
Часто задаваемые вопросы о гамме Чарльза Пойнтона

Myndex
источник
@Rotem спасибо - я увидел несколько странных и неполных заявлений и чувствовал, что было бы полезно закрепить их, особенно потому, что эта тема по-прежнему занимает высокие позиции в поисковых системах.
Myndex
Я создал демонстрацию, сравнивая BT.601 Luma и CIE 1976 L * Perceptual Grey , используя несколько команд MATLAB:Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem
@Myndex Я использовал твои формулы, чтобы добраться до L *, но я все еще получаю некоторые странные результаты, независимо от того, какую формулу я использую ... С твоим L * из # d05858 темнее, чем L * из # c51c2a ... Есть ли какие-нибудь способ получить это право? Почему никакая формула не работает так, как ожидалось? :(
Сяхан
1
@asdfasdfads Да, L*a*b*не учитывает ряд психофизических признаков. Эффект Гельмгольца-Кольрауша один, но есть много других. CIELAB - это не «полная» модель оценки изображений. В своем посте я пытался как можно полнее охватить основные понятия, не углубляясь в очень глубокие подробности. Модель Ханта, модели Фэйрчайлда и другие делают более полную работу, но они также значительно сложнее.
Myndex
1
@Myndex, неважно, моя реализация была основана на усталости, и мои плохие результаты были получены :( Большое спасибо за вашу помощь и ваш пост, который имеет большую ценность!
sjahan
11

Я нашел этот код (написанный на C #), который отлично справляется с расчетом «яркости» цвета. В этом сценарии код пытается определить, нужно ли поместить белый или черный текст поверх цвета.

sitesbyjoe
источник
1
Это именно то, что мне было нужно. Я делал классическую демонстрацию «цветные полосы» и хотел пометить их поверх цвета с лучшим выбором черного или белого цвета!
RufusVS
10

Интересно, что эта формулировка для RGB => HSV просто использует v = MAX3 (r, g, b). Другими словами, вы можете использовать максимум (r, g, b) как V в HSV.

Я проверил, и на странице 575 в Hearn & Baker они так же вычисляют «ценность».

От Хирн и Бейкер, стр. 319

bobobobo
источник
Просто для записи ссылка мертва, архивная версия здесь - web.archive.org/web/20150906055359/http://…
Питер
ВПГ не воспринимается равномерно (и даже не близко). Он используется только как «удобный» способ настройки цвета, но он не имеет отношения к восприятию, а V не относится к истинному значению L или Y (CIE Luminance).
Myndex
9

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

Вот прямая, но точная реализация PHP формул относительной яркости и контрастности WCAG 2.0 SC 1.4.3 . Он выдает значения, которые подходят для оценки соотношений, необходимых для соответствия WCAG, как на этой странице , и как таковые подходят и подходят для любого веб-приложения. Это тривиально портировать на другие языки.

/**
 * Calculate relative luminance in sRGB colour space for use in WCAG 2.0 compliance
 * @link http://www.w3.org/TR/WCAG20/#relativeluminancedef
 * @param string $col A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <marcus@synchromedia.co.uk>
 */
function relativeluminance($col) {
    //Remove any leading #
    $col = trim($col, '#');
    //Convert 3-digit to 6-digit
    if (strlen($col) == 3) {
        $col = $col[0] . $col[0] . $col[1] . $col[1] . $col[2] . $col[2];
    }
    //Convert hex to 0-1 scale
    $components = array(
        'r' => hexdec(substr($col, 0, 2)) / 255,
        'g' => hexdec(substr($col, 2, 2)) / 255,
        'b' => hexdec(substr($col, 4, 2)) / 255
    );
    //Correct for sRGB
    foreach($components as $c => $v) {
        if ($v <= 0.04045) {
            $components[$c] = $v / 12.92;
        } else {
            $components[$c] = pow((($v + 0.055) / 1.055), 2.4);
        }
    }
    //Calculate relative luminance using ITU-R BT. 709 coefficients
    return ($components['r'] * 0.2126) + ($components['g'] * 0.7152) + ($components['b'] * 0.0722);
}

/**
 * Calculate contrast ratio acording to WCAG 2.0 formula
 * Will return a value between 1 (no contrast) and 21 (max contrast)
 * @link http://www.w3.org/TR/WCAG20/#contrast-ratiodef
 * @param string $c1 A 3 or 6-digit hex colour string
 * @param string $c2 A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <marcus@synchromedia.co.uk>
 */
function contrastratio($c1, $c2) {
    $y1 = relativeluminance($c1);
    $y2 = relativeluminance($c2);
    //Arrange so $y1 is lightest
    if ($y1 < $y2) {
        $y3 = $y1;
        $y1 = $y2;
        $y2 = $y3;
    }
    return ($y1 + 0.05) / ($y2 + 0.05);
}
Синхронная
источник
почему вы предпочитаете определение w3c? лично я реализовал и CCIR 601, и рекомендованный w3c, и я был гораздо более доволен результатами CCIR 601
user151496
1
Потому что, как я уже сказал, это рекомендуется как W3C, так и WCAG?
Синхро
1
Формула W3C неверна на нескольких уровнях. Это не принимает во внимание человеческое восприятие, они используют «простой» контраст, используя яркость, которая является линейной и совсем не воспринимаемой равномерно. Между прочим, похоже, что они основаны на некоторых стандартах 1988 года (!!!), которые не актуальны сегодня (эти стандарты были основаны на монохромных мониторах, таких как зеленый / черный), и относились к общему контрасту от включения до выключения , не считая оттенков серого, ни цветов).
Myndex
1
Это полная чушь. Люма обладает особым восприятием - поэтому она имеет разные коэффициенты для красного, зеленого и синего. Возраст не имеет к этому никакого отношения - превосходное цветовое пространство восприятия CIE Lab датируется 1976 годом. Пространство W3C не так хорошо, однако это хорошее практическое приближение, которое легко вычислить. Если у вас есть что-то конструктивное, разместите это вместо пустой критики.
Synchro
3
Просто чтобы добавить / обновить : в настоящее время мы исследуем алгоритмы замены, которые лучше моделируют контраст восприятия (обсуждение в Github Issue 695) . Однако в качестве отдельной проблемы FYI пороговое значение для sRGB составляет 0,04045 , а не 0,03928, на которое ссылалась устаревшая ранняя версия sRGB. В авторитетном стандарте IEC используется 0.04045, и для исправления этой ошибки в WCAG ожидается запрос на извлечение. (ссылка: IEC 61966-2-1: 1999) Это в выпуске Github 360, хотя следует упомянуть, что в 8-битной разнице нет фактической разницы - на ближнем конце потока 360 у меня есть диаграммы ошибок, включая 0.04045 / 0.03928 в 8-битной.
Myndex
8

Чтобы добавить то, что сказали все остальные:

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

Разница в яркости между поглощением гаммы и выполнением правильной гаммы составляет до 20% в темных серых тонах.

Нильс Пипенбринк
источник
2

Я решал аналогичную задачу сегодня в JavaScript. Я остановился на этой getPerceivedLightness(rgb)функции для цвета HEX RGB. Он имеет дело с эффектом Гельмгольца-Кольрауша с помощью формул Фэрчайлда и Перротты для коррекции яркости.

/**
 * Converts RGB color to CIE 1931 XYZ color space.
 * https://www.image-engineering.de/library/technotes/958-how-to-convert-between-srgb-and-ciexyz
 * @param  {string} hex
 * @return {number[]}
 */
export function rgbToXyz(hex) {
    const [r, g, b] = hexToRgb(hex).map(_ => _ / 255).map(sRGBtoLinearRGB)
    const X =  0.4124 * r + 0.3576 * g + 0.1805 * b
    const Y =  0.2126 * r + 0.7152 * g + 0.0722 * b
    const Z =  0.0193 * r + 0.1192 * g + 0.9505 * b
    // For some reason, X, Y and Z are multiplied by 100.
    return [X, Y, Z].map(_ => _ * 100)
}

/**
 * Undoes gamma-correction from an RGB-encoded color.
 * https://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation
 * /programming/596216/formula-to-determine-brightness-of-rgb-color
 * @param  {number}
 * @return {number}
 */
function sRGBtoLinearRGB(color) {
    // Send this function a decimal sRGB gamma encoded color value
    // between 0.0 and 1.0, and it returns a linearized value.
    if (color <= 0.04045) {
        return color / 12.92
    } else {
        return Math.pow((color + 0.055) / 1.055, 2.4)
    }
}

/**
 * Converts hex color to RGB.
 * /programming/5623838/rgb-to-hex-and-hex-to-rgb
 * @param  {string} hex
 * @return {number[]} [rgb]
 */
function hexToRgb(hex) {
    const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    if (match) {
        match.shift()
        return match.map(_ => parseInt(_, 16))
    }
}

/**
 * Converts CIE 1931 XYZ colors to CIE L*a*b*.
 * The conversion formula comes from <http://www.easyrgb.com/en/math.php>.
 * https://github.com/cangoektas/xyz-to-lab/blob/master/src/index.js
 * @param   {number[]} color The CIE 1931 XYZ color to convert which refers to
 *                           the D65/2° standard illuminant.
 * @returns {number[]}       The color in the CIE L*a*b* color space.
 */
// X, Y, Z of a "D65" light source.
// "D65" is a standard 6500K Daylight light source.
// https://en.wikipedia.org/wiki/Illuminant_D65
const D65 = [95.047, 100, 108.883]
export function xyzToLab([x, y, z]) {
  [x, y, z] = [x, y, z].map((v, i) => {
    v = v / D65[i]
    return v > 0.008856 ? Math.pow(v, 1 / 3) : v * 7.787 + 16 / 116
  })
  const l = 116 * y - 16
  const a = 500 * (x - y)
  const b = 200 * (y - z)
  return [l, a, b]
}

/**
 * Converts Lab color space to Luminance-Chroma-Hue color space.
 * http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html
 * @param  {number[]}
 * @return {number[]}
 */
export function labToLch([l, a, b]) {
    const c = Math.sqrt(a * a + b * b)
    const h = abToHue(a, b)
    return [l, c, h]
}

/**
 * Converts a and b of Lab color space to Hue of LCH color space.
 * /programming/53733379/conversion-of-cielab-to-cielchab-not-yielding-correct-result
 * @param  {number} a
 * @param  {number} b
 * @return {number}
 */
function abToHue(a, b) {
    if (a >= 0 && b === 0) {
        return 0
    }
    if (a < 0 && b === 0) {
        return 180
    }
    if (a === 0 && b > 0) {
        return 90
    }
    if (a === 0 && b < 0) {
        return 270
    }
    let xBias
    if (a > 0 && b > 0) {
        xBias = 0
    } else if (a < 0) {
        xBias = 180
    } else if (a > 0 && b < 0) {
        xBias = 360
    }
    return radiansToDegrees(Math.atan(b / a)) + xBias
}

function radiansToDegrees(radians) {
    return radians * (180 / Math.PI)
}

function degreesToRadians(degrees) {
    return degrees * Math.PI / 180
}

/**
 * Saturated colors appear brighter to human eye.
 * That's called Helmholtz-Kohlrausch effect.
 * Fairchild and Pirrotta came up with a formula to
 * calculate a correction for that effect.
 * "Color Quality of Semiconductor and Conventional Light Sources":
 * https://books.google.ru/books?id=ptDJDQAAQBAJ&pg=PA45&lpg=PA45&dq=fairchild+pirrotta+correction&source=bl&ots=7gXR2MGJs7&sig=ACfU3U3uIHo0ZUdZB_Cz9F9NldKzBix0oQ&hl=ru&sa=X&ved=2ahUKEwi47LGivOvmAhUHEpoKHU_ICkIQ6AEwAXoECAkQAQ#v=onepage&q=fairchild%20pirrotta%20correction&f=false
 * @return {number}
 */
function getLightnessUsingFairchildPirrottaCorrection([l, c, h]) {
    const l_ = 2.5 - 0.025 * l
    const g = 0.116 * Math.abs(Math.sin(degreesToRadians((h - 90) / 2))) + 0.085
    return l + l_ * g * c
}

export function getPerceivedLightness(hex) {
    return getLightnessUsingFairchildPirrottaCorrection(labToLch(xyzToLab(rgbToXyz(hex))))
}
catamphetamine
источник
1

Цветовое пространство HSV должно помочь, см. Статью в Википедии зависимости от языка, на котором вы работаете, вы можете получить преобразование библиотеки.

H является оттенком, который является числовым значением для цвета (то есть красный, зеленый ...)

S - насыщенность цвета, т. Е. Насколько он интенсивный

V - это «яркость» цвета.

Ян Хопкинсон
источник
7
Проблема с цветовым пространством HSV заключается в том, что вы можете иметь одинаковую насыщенность и значение, но разные оттенки для синего и желтого . Желтый намного ярче синего. То же самое касается HSL.
Ян Бойд
hsv дает вам «яркость» цвета в техническом смысле. в перцептуальной яркости hsv действительно не получается
user151496
HSV и HSL не точны в восприятии (и даже не близки). Они полезны для «управления» для корректировки относительного цвета, но не для точного предсказания воспринимаемой легкости. Используйте L * от CIELAB для восприятия легкости.
Myndex
1

Значение яркости RGB = 0,3 R + 0,59 G + 0,11 B

http://www.scantips.com/lumin.html

Если вы ищете, насколько близок белый цвет, вы можете использовать Евклидово расстояние от (255, 255, 255)

Я думаю, что цветовое пространство RGB заметно неоднородно по отношению к евклидову расстоянию L2. Унифицированные пространства включают CIE LAB и LUV.


источник
1

Формула обратной гаммы Джайва Дадсона должна быть удалена с половиной корректировки при реализации в Javascript, то есть возвращение функции gam_sRGB должно возвращать int (v * 255); не вернуть int (v * 255 + .5); Полу-корректировка округляется, и это может привести к значению, слишком высокому для R = G = B, то есть для триады серого цвета. Преобразование в оттенки серого в триаде R = G = B должно давать значение, равное R; это одно из доказательств того, что формула верна. См. Nine Shades of Greyscale для формулы в действии (без половинной корректировки).

Дейв Кольер
источник
Похоже, вы знаете свои вещи, поэтому я удалил +0,5
Джайв
Я сделал эксперимент. В C ++ ему нужно +0,5, поэтому я вернул его обратно. Я добавил комментарий о переводе на другие языки.
Джайв
1

Интересно, как эти коэффициенты RGB были определены. Я сам провел эксперимент и в итоге получил следующее:

Y = 0.267 R + 0.642 G + 0.091 B

Близко, но очевидно отличается от давно установленных коэффициентов МСЭ. Интересно, могут ли эти коэффициенты быть разными для каждого наблюдателя, потому что у всех нас может быть разное количество колбочек и палочек на сетчатке в наших глазах, и особенно соотношение между разными колбочками может отличаться.

Для справки:

МСЭ BT.709:

Y = 0.2126 R + 0.7152 G + 0.0722 B

МСЭ BT.601:

Y = 0.299 R + 0.587 G + 0.114 B

Я выполнил тест, быстро переместив маленькую серую полоску на ярко-красный, ярко-зеленый и ярко-синий фон, и отрегулировал серый, пока он не смешался настолько, насколько это было возможно. Я также повторил этот тест с другими оттенками. Я повторил тест на разных дисплеях, даже с фиксированным гамма-фактором 3,0, но для меня все выглядит одинаково. Более того, коэффициенты МСЭ буквально не соответствуют моим глазам.

И да, у меня, видимо, нормальное цветовое зрение.

вихревой
источник
В своих экспериментах линеаризовали ли вы сначала удаление гамма-компонента? Если вы этого не сделаете, это может объяснить ваши результаты. НО ТАКЖЕ, коэффициенты связаны с экспериментами CIE 1931 года, и это в среднем 17 наблюдателей, так что да, есть индивидуальная разница в результатах.
Myndex
1

Вот немного кода C, который должен правильно рассчитать воспринимаемую яркость.

// reverses the rgb gamma
#define inverseGamma(t) (((t) <= 0.0404482362771076) ? ((t)/12.92) : pow(((t) + 0.055)/1.055, 2.4))

//CIE L*a*b* f function (used to convert XYZ to L*a*b*)  http://en.wikipedia.org/wiki/Lab_color_space
#define LABF(t) ((t >= 8.85645167903563082e-3) ? powf(t,0.333333333333333) : (841.0/108.0)*(t) + (4.0/29.0))


float
rgbToCIEL(PIXEL p)
{
   float y;
   float r=p.r/255.0;
   float g=p.g/255.0;
   float b=p.b/255.0;

   r=inverseGamma(r);
   g=inverseGamma(g);
   b=inverseGamma(b);

   //Observer = 2°, Illuminant = D65 
   y = 0.2125862307855955516*r + 0.7151703037034108499*g + 0.07220049864333622685*b;

   // At this point we've done RGBtoXYZ now do XYZ to Lab

   // y /= WHITEPOINT_Y; The white point for y in D65 is 1.0

    y = LABF(y);

   /* This is the "normal conversion which produces values scaled to 100
    Lab.L = 116.0*y - 16.0;
   */
   return(1.16*y - 0.16); // return values for 0.0 >=L <=1.0
}
EddingtonsMonkey
источник
0

Пожалуйста, определите яркость. Если вы ищете, насколько близок белый цвет, вы можете использовать Евклидово расстояние от (255, 255, 255)

Бен С
источник
1
Нет, вы не можете использовать евклидово расстояние между значениями sRGB, sRGB не является перцепционно однородным декартовым / векторным пространством. Если вы хотите использовать евклидово расстояние в качестве меры разницы в цвете, вам нужно как минимум преобразовать в CIELAB или, что еще лучше, использовать CAM, такой как CIECAM02.
Myndex
0

«V» HSV, вероятно, то, что вы ищете. MATLAB имеет функцию rgb2hsv, а ранее цитированная статья в Википедии полна псевдокода. Если преобразование RGB2HSV невозможно, менее точной моделью будет версия изображения в градациях серого.

Иаков
источник
0

Эта ссылка подробно объясняет все, в том числе, почему эти константы множителя существуют до значений R, G и B.

Изменить: Здесь также есть объяснение одному из ответов (0,299 * R + 0,587 * G + 0,114 * B).

dsignr
источник
0

Чтобы определить яркость цвета с помощью R, я конвертирую системный цвет RGB в системный цвет HSV.

В моем сценарии я использую системный код HEX и раньше по другой причине, но вы также можете начать с системного кода RGB с помощью rgb2hsv {grDevices}. Документация здесь .

Вот эта часть моего кода:

 sample <- c("#010101", "#303030", "#A6A4A4", "#020202", "#010100")
 hsvc <-rgb2hsv(col2rgb(sample)) # convert HEX to HSV
 value <- as.data.frame(hsvc) # create data.frame
 value <- value[3,] # extract the information of brightness
 order(value) # ordrer the color by brightness
Пьер-Луис Стенгер
источник
0

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

sqrt(coefficient * (colour_value^2))

не

sqrt((coefficient * colour_value))^2

Доказательством этого является преобразование триады R = G = B в шкалу серого R. Это будет верно только в том случае, если вы возводите в квадрат значение цвета, а не коэффициент времени, равный временному значению. См Девять оттенков серого

Дейв Кольер
источник
5
есть несоответствия в скобках
log0
если коэффициент, который вы используете, не является квадратным корнем правильного коэффициента.
RufusVS