Формула для визначення яскравості кольору RGB


387

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


8
Сприйнята яскравість - це те, що я думаю, що шукаю, дякую.
robmerica

2
Є гарна стаття ( Маніпулювання кольорами в .NET - Частина 1 ) про кольорові простори та розмови між ними, включаючи як теорію, так і код (C #). Для відповіді дивіться тему Перетворення між моделями у статті.
підкреслюйте

4
Я був членом багато років, і ніколи раніше цього не робив. Чи можу я запропонувати вам переглянути відповіді та подумати, яку з них прийняти?
Jive Dadson

Відповіді:


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. Вони не конвертуються так, як ви думаєте.
alex странно

4
Не вірно. Перш ніж застосовувати лінійне перетворення, слід спочатку застосувати зворотну функцію гамми для колірного простору. Потім після застосування лінійної функції застосовується гамма-функція.
Jive Dadson

6
В останній формулі це (0,299 * R) ^ 2 або це 0,299 * (R ^ 2)?
Кайзер Созай

3
@KaizerSozay Як написано тут, це означало б 0.299*(R^2)(адже експоненція переходить до множення)
Дантевг

298

Я думаю, що ви шукаєте, це формула перетворення RGB -> Luma .

Фотометричний / цифровий МСЕ 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, другий - (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

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

Четверта картина - WCAG 2,0 SC 1.4.3 відносна яскравість і контрастність формула (див @ Synchro в відповідь тут )

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

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

Порівняння алгоритму яскравості


3
Для мене це найкраща відповідь, тому що ви використовуєте малюнок малюнка, який дозволяє вам сприймати, якщо різні відтінки надаються з такою ж яскравістю. Для мене та мого теперішнього монітора 3-я картина є "найкраще виглядає", оскільки вона також швидша, ніж 4-та, це плюс
CoffeDeveloper

8
Зображення порівняння невірне, оскільки ви не ввели правильний вхід для всіх функцій. Перша функція вимагає лінійного введення RGB; Я можу відтворити ефект обв'язки лише за умови надання нелінійного (тобто з коригуванням гами) RGB. Виправляючи цю проблему, ви не маєте артефактів, що перебувають у смужці, і 1-а функція - явний переможець.
Макс

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.
Jive Dadson

8
JMD - в рамках роботи в лабораторії візуального сприйняття я здійснив прямі вимірювання яскравості на моніторах ЕПТ і можу підтвердити, що в нижній частині діапазону значень є лінійна область яскравості.
Джеррі Федерспіель

2
Я знаю, що це дуже старе, але його все ще потрібно шукати. Я не думаю, що це може бути правильним. Чи не повинен сірий (255,255,255) = сірий (255,0,0) + сірий (0,255,0) + сірий (0,0255)? Це не так.
DCBillen

2
@DCBillen: ні, оскільки значення знаходяться в нелінійному просторі з корекцією гамма sRGB, ви не можете просто їх скласти. Якщо ви хотіли додати їх, вам слід зробити це перед тим, як викликати gam_sRGB.
rdb

1
@DCBillen Rdb правильний. Спосіб їх додавання показаний у функції int grey (int r, int g, int b), яка "відкликає" gam_sRGB. Мене болить, що через чотири роки правильна відповідь оцінюється настільки низько. :-) Насправді .. Я переберу це.
Jive Dadson

45

Відповідь "Прийнято" неправильна та неповна

Єдиними точними відповідями є відповіді @ jive-dadson та @EddingtonsMonkey та підтримка @ nils-pipenbrinck . Інші відповіді (включаючи прийняті) стосуються посилань на джерела, які є неправильними, неактуальними, застарілими або порушеними.

Коротко:

  • sRGB перед застосуванням коефіцієнтів необхідно ЛІНІЯНЗАЛІЗУВАТИ .
  • Яскравість (L або Y) лінійна, як і світло.
  • Сприйнята легкість (L *) нелінійна, як і людське сприйняття.
  • ВПГ та ХСЛ навіть не є віддаленими точними щодо сприйняття.
  • Стандарт IEC для sRGB визначає поріг 0,04045, це НЕ 0,03928 (це було від застарілого раннього проекту).
  • Щоб бути корисними (тобто відносно сприйняття) , евклідові відстані потребують перцептивно рівномірного декартового векторного простору, такого як CIELAB. sRGB - це не один.

Далі правильна і повна відповідь:

Оскільки ця нитка виявляється високо в пошукових системах, я додаю цю відповідь, щоб уточнити різні помилки з цього приводу.

Яскравість - це перцептивний атрибут, він не має прямої міри.

Сприйнята легкість вимірюється деякими моделями зору, такими як CIELAB, тут L * (Lstar) - це міра перцептивної легкості і нелінійна для наближення кривої реакції людського зору до нелінійної відповіді.

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

Luma ( prime) - це гамма-кодований зважений сигнал, який використовується у деяких відеокодуваннях. Його не слід плутати з лінійною яскравістю.

Гамма або крива передачі (TRC) - крива, яка часто схожа на перцептивну криву, і зазвичай застосовується до даних зображення для зберігання або трансляції для зменшення сприйнятого шуму та / або поліпшення використання даних (та пов'язаних з цим причин).

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


ЗНАЙДЕНО СВЯТЛЕННЯ:

... Тому що, мабуть, воно десь загубилося ...

Крок перший:

Перетворіть усі значення sRGB 8-бітних цілих чисел у десяткові 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));
        }
    }

Крок третій:

Щоб знайти Luminance (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
Часто відповіді про гамму Чарльза Пойнтона


@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 ... Чи є спосіб отримати це право? Чому жодна формула не працює так, як очікувалося? :(
sjahan

1
@asdfasdfads Так, L*a*b*не враховує низку психофізичних ознак. Ефект Гельмгольца-Кольрауша є одним, але є багато інших. CIELAB - це не "повна" модель оцінки зображень жодним чином. У своєму дописі я намагався якомога повніше висвітлити основні поняття, не впадаючи в дуже глибокі деталі. Модель Ханта, моделі Ферчільда ​​та інші виконують більш повну роботу, але також є значно складнішою.
Myndex

1
@Myndex, незважаючи ні на що, моя реалізація була заснована на втомі, і мої погані результати випливали з цього :( Дякую вам за допомогу та ваш пост, який має велике значення!
sjahan

11

Я знайшов цей код (написаний на C #), який відмінно справляється з обчисленням "яскравості" кольору. У цьому сценарії код намагається визначити, чи слід розміщувати білий або чорний текст над кольором.


1
Це саме те, що мені було потрібно. Я робив класичну демонстрацію "кольорових смуг", і хотів позначити їх поверх кольору найкращим вибором чорно-білого кольору!
RufusVS

10

Цікаво, що ця рецептура для RGB => HSV просто використовує v = MAX3 (r, g, b). Іншими словами, ви можете використовувати максимум (r, g, b) як V у ВПГ.

Я перевірив, і на сторінці 575 Hearn & Baker це так, як вони обчислюють "Value".

Від Hearn & Baker, сторінка 319


Тільки для запису посилання мертве, версія архіву тут - 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
користувач151496

1
Тому що, як я вже сказав, це рекомендують і W3C, і WCAG?
Синхро

1
Формула W3C невірна на кількох рівнях. Це не враховує людське сприйняття, вони використовують "простий" контраст, використовуючи яскравість, яка лінійна і зовсім не однаково сприймається. Крім усього іншого, видається, що вони ґрунтувались на деяких стандартах, старіших у 1988 році (!!!), які не є актуальними сьогодні (ці стандарти базувалися на монохромних моніторах, таких як зелений / чорний, і посилаються на загальну контрастність від «до» , не враховуючи відтінків сірого чи кольорів).
Myndex

1
Це повне сміття. Люма конкретно сприймається - тому вона має різні коефіцієнти для червоного, зеленого та синього. Вік нічого спільного з цим не має - відмінний перцептивний кольоровий простір лабораторії CIE датується 1976 р. Простір W3C не настільки гарний, проте це хороше практичне наближення, яке легко обчислити. Якщо ви можете запропонувати щось конструктивне, опублікуйте це замість порожньої критики.
Synchro

3
Просто для додання / оновлення : зараз ми досліджуємо алгоритми заміни, які краще моделюють сприйняття контрасту (обговорення в Github Issue 695) . Однак, як окремий випуск FYI, поріг для sRGB становить 0,04045 , а не 0,03928, на який посилався застарілий ранній проект sRGB. Авторитетний IEC std використовує 0,04045, і надходить запит на виправлення, щоб виправити цю помилку в WCAG. (посилання: IEC 61966-2-1: 1999) Це у випуску 360 Github, хоча, щоб сказати, у 8bit немає фактичної різниці - біля кінця потоку 360 у мене є діаграми помилок, включаючи 0,04045 / 0,03928 в 8bit.
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))))
}

1

Кольоровий простір HSV повинен зробити цю справу, перегляньте статтю у Вікіпедії залежно від мови, на якій ви працюєте, ви можете отримати конверсію бібліотеки.

H - відтінок, який є числовим значенням для кольору (тобто червоний, зелений ...)

S - насиченість кольору, тобто наскільки він "інтенсивний"

V - «яскравість» кольору.


7
Проблема кольорового простору HSV полягає в тому, що для синього та жовтого кольорів ви можете мати однакову насиченість та значення, але різного відтінку . Жовтий набагато яскравіший, ніж синій. Те саме стосується HSL.
Ян Бойд

hsv надає вам "яскравість" кольору в технічному сенсі. у сприйнятій яскравості hsv дійсно не вдається
користувач151496

HSV та HSL не є перцептивно точними (і це навіть не близько). Вони корисні для "елементів керування" для регулювання відносного кольору, але не для точного прогнозування перцептивної легкості. Використовуйте L * від CIELAB для сприйняття легкості.
Миндекс

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; це один доказ того, що формула є дійсною. Дивіться дев'ять відтінків відтінків сірого за формулою в дії (без напівкорегування).


Здається, ви знаєте свої речі, тому я зняв +0.5
Jive Dadson

Я зробив експеримент. У C ++ йому потрібні +0,5, тому я поверну його назад. Я додав коментар щодо перекладу на інші мови.
Jive Dadson

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

Ось трохи коду С, який повинен правильно обчислити сприйняту яскравість.

// 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
}

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)


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.