Как эффективно рассчитать разницу контрастности человеческого глаза для значений RGB?
Чтобы проверить, будут ли два цвета в оттенках серого слишком близко, чтобы их различил человеческий глаз. Я хочу иметь возможность генерировать предупреждение пользователю, если выбраны "опасные" цвета. Следовательно, основываясь на результате, мы можем решить, следует ли для людей с плохим зрением изменить один из двух цветов на белый или черный, чтобы улучшить читаемый контраст. Например, шестнадцатеричные цвета #9d5fb0
(фиолетовый) и #318261
(зеленый) превратится в почти такой же серый тон. В HSB значение B просто на 1% отличается от другого, и поэтому здоровый человеческий глаз не может увидеть разницу. Или для того же 8-битного значения K в этом случае отличается на 2%.
Я узнал, что метод освещения - более сложный способ оценки серых тонов, как человеческий глаз видит цвета. Тем не менее, как сделать это программно выходит за рамки моего текущего понимания. Я мог бы написать это либо PHP
или же JS
как только я понимаю математику
Для того, чтобы выбрать значения из CSS
с экрана pixel
или из файла в виде изображения object
Я думаю, мы всегда должны обрабатывать ввод как RGB, верно?
что-то вроде:
$result = grayScaleDifference('#9d5fb0','#318261');
или же
$result = 8bitK_difference('#9d5fb0','#318261');
или же
$result = luminanceDifference('#9d5fb0','#318261');
Итак, какова лучшая формула стиля сценария для сравнения их без изменения или преобразования реального изображения или цветных объектов?
5 ответов
ПОСЛЕДУЮЩИЙ ОТВЕТ
Я публикую это как дополнительный ответ, чтобы не только уточнить мой первоначальный ответ (который я также только что отредактировал), но также чтобы добавить фрагменты кода различных концепций. Каждый шаг в процессе от R´G´B´ до Y важен, и также должен быть в указанном порядке, иначе результаты потерпят неудачу.
ОПРЕДЕЛЕНИЯ:
sRGB: sRGB - это трехцветная цветовая модель, которая является стандартом для Интернета и используется на большинстве компьютерных мониторов. Он использует те же основные цвета и белую точку, что и Rec709, стандарт для HDTV. sRGB отличается от Rec709 только кривой передачи, часто называемой гаммой.
Гамма: это кривая, используемая с различными методами кодирования изображений для хранения и передачи. Это часто похоже на кривую восприятия человеческого зрения. В цифровом гамма-эффект состоит в том, чтобы придать больший вес темным областям изображения, чтобы они определялись большим количеством битов, чтобы избежать таких артефактов, как "полосатость".
Яркость: (обозначено буквой L или Y): линейная мера или представление света (то есть НЕТ гамма-кривой). В качестве меры обычно это кд / м 2. Как представление, это Y, как в CIEXYZ, и обычно от 0 (черный) до 100 (белый). Яркость характеризуется спектральным взвешиванием, основанным на восприятии человеком различных длин волн света. Тем не менее, яркость является линейной с точки зрения яркости / темноты - то есть, если 100 фотонов света измеряют 10, то 20 будет 200 фотонов света.
L* (он же Lstar): воспринимаемая легкость, как определено в CIELAB (L*a*b*). Если яркость является линейной с точки зрения количества света, L* основана на восприятии и, следовательно, нелинейна с точки зрения количества света. с кривой, предназначенной для соответствия фотопическому зрению человеческого глаза (прибл. гамма составляет ^0,43).
Яркость против L*: 0 и 100 одинаковы как по яркости (обозначено Y или L), так и по яркости (обозначено L*), но в середине они очень разные. То, что мы называем средним серым, находится в самой середине L* на 50, но это относится к 18,4 в яркости (Y). В sRGB это # 777777 или 46,7%.
Контраст: термин для определения разницы между двумя значениями L или Y. Есть несколько методов и стандартов для контраста. Одним из распространенных методов является контраст Вебера, который равен ΔL/L. Контрастность обычно указывается в виде соотношения (3:1) или процента (70%).
ПОЛУЧЕНИЕ СВЕТА (Y) ИЗ sRGB
STEP ZERO (un - HEX)
При необходимости преобразуйте значение цвета HEX в триплет целочисленных значений, где #00 = 0
а также #FF = 255
,
ШАГ ПЕРВЫЙ (от 8 до десятичного числа)
Преобразуйте 8-битные значения sRGB в десятичные, разделив на 255:
R´ десятичный = R´ 8bit / 255 G´ десятичный = G´ 8bit / 255 B´ десятичный = B´ 8bit / 255
Если ваши значения sRGB 16-битные, преобразуйте их в десятичные, разделив на 65535.
ШАГ ВТОРОЙ (Linearize, Simple Version)
Поднимите каждый цветной канал до уровня 2,2, так же, как дисплей sRGB. Это хорошо для большинства приложений. Но если вам нужно совершить многократные поездки в пространство гамма-кодирования sRGB, используйте более точные версии ниже.
R´ ^2.2 = R lin G´ ^2.2 = G lin B´ ^2.2 = B lin
ШАГ ВТОРОЙ (линеаризация, точная версия)
Используйте эту версию вместо простой версии ^2.2, описанной выше, если вы выполняете манипуляции с изображениями и совершаете многократные циклические переходы в и из гамма-кодированного пространства.
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 Math.pow((( colorChannel + 0.055)/1.055),2.4));
}
}
ТРЕТИЙ ШАГ (спектрально-взвешенная яркость)
У нормального человеческого глаза есть три типа колбочек, которые чувствительны к красному, зеленому и синему свету. Но наша спектральная чувствительность не одинакова, так как мы наиболее чувствительны к зеленому (555 нм), а синий - последнее отдаленное место. Яркость спектрально взвешена, чтобы отразить это, используя следующие коэффициенты:
R lin * 0,2126 + G lin * 0,7152 + B lin * 0,0722 = Y = L
Умножьте каждый линеаризованный цветовой канал на их коэффициент и сложите их все вместе, чтобы найти L, Яркость.
ШАГ ЧЕТВЕРТЫЙ (Определение контрастности)
Существует множество различных способов определения контраста, а также различные стандарты. Некоторые уравнения работают лучше, чем другие, в зависимости от конкретного применения.
WCAG
Текущий стандарт веб-страниц, перечисленный в WCAG 2.0 и 2.1, отличается от простого:
C = ((L светлее + 0,05) / (L темнее + 0,05)): 1
Это дает соотношение, и стандарты WCAG определяют 3: 1 для нетекстовых и 4,5:1 для текстовых.
Однако это слабый пример по ряду причин. Я официально отмечаю недостатки в текущем выпуске GitHub (695) и изучаю альтернативы.
Модифицированный Вебер
Модифицированный Вебер Hwang/Peli обеспечивает лучшую оценку контрастности, поскольку он применяется к компьютерным мониторам / sRGB.
C = (L светлее - L темнее) / (L светлее + 0,1)
Обратите внимание, что я выбрал коэффициент вспышки 0,1 вместо 0,05, основываясь на некоторых недавних экспериментах. Это значение TBD, и другое значение может быть лучше.
LAB Difference
Другой альтернативой, которая мне нравится больше, чем другие, является преобразование линеаризованной яркости (L) в L*, которая представляет собой перцептуальную легкость, а затем просто вычитание одного из другого, чтобы найти разницу.
Преобразовать Y в L*:
function YtoLstar(Y) {
// Send this function a luminance value between 0.0 and 1.0,
// and it returns L* - 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 Math.pow(Y,(1/3)) * 116 - 16;
}
}
После того, как вы преобразовали L в L*, полезная контрастная фигура будет просто:
C = L* светлее - L* темнее
Здесь результаты, возможно, должны быть масштабированы, чтобы быть похожими на другие методы. Масштабирование около 1,6 или 1,7, кажется, хорошо работает.
Существует ряд других методов определения контрастности, но они являются наиболее распространенными. Некоторые приложения, тем не менее, будут лучше с другими методами контраста. Некоторые другие - это контраст Майклсона, длина восприятия контраста (PCL) и Боуман / Саполински.
ТАКЖЕ, если вы ищете различия в цвете помимо различий в яркости или освещенности, то у CIELAB есть несколько полезных методов в этом отношении.
ПОБОЧНЫЕ ЗАМЕЧАНИЯ:
Усреднение RGB Нет, Буэно!
OP 2x2p упомянул обычно цитируемое уравнение для создания оттенков серого цвета как:
СЕРЫЙ = круглый ((R + G + B) / 3);
Он указал на то, насколько неточным это казалось, да и вообще - это совершенно неправильно. Спектральные веса R, G и B являются существенными и не могут быть пропущены. ЗЕЛЕНЫЙ - это более высокая яркость, чем СИНИЙ, ПОРЯДОК МАГНИТА. Вы не можете просто сложить все три канала вместе, разделить на три и получить что-нибудь, близкое к фактической яркости определенного цвета.
Я полагаю, что путаница по этому поводу могла возникнуть из-за контроля цвета, известного как HSI (оттенок, насыщенность, интенсивность). Но этот контроль не является (и никогда не должен быть) перцепционно однородным!!! HSI, как и HSV, - это просто "удобство" для манипулирования значениями цвета в компьютере. Они не одинаковы по восприятию, и математика, которую они используют, предназначена исключительно для поддержки "простого" способа настройки значений цвета в программном обеспечении.
Образцы цветов OP
2x2p опубликовал свой код, используя '#318261','#9d5fb0' в качестве тестовых цветов. Вот как они выглядят в моей электронной таблице вместе с каждым значением на каждом этапе процесса преобразования (используя "точный" метод sRGB):
Оба близки к среднему серому цвета #777777. Обратите также внимание, что, хотя яркость L составляет всего 18, воспринимаемая яркость L* равна 50.
КОНТРАСТ И ВЗГЛЯД ЛЮМИНАНСА
То, что вы ищете, это как оценить контрастность яркости.
Вы определенно на правильном пути - 6% мужчин имеют дальтонизм, и они полагаются на контраст яркости, а не цветовой контраст. У меня есть диаграмма, которая демонстрирует эту проблему.
И просто к сведению термин "яркость", а не светимость. Светимость относится к свету, испускаемому во времени, часто используемому в астрономии. Когда мы говорим о колориметрии, мы используем термин "яркость", другой показатель освещенности, определенный CIEXYZ (CIE 1931).
Как это происходит, я нахожусь в процессе исследования методов оценки контрастности, чтобы обеспечить некоторые новые и более точные стандарты. Вы можете следить за прогрессом на GitHub и на моей странице исследования восприятия.
Это не так просто, как можно подумать, поскольку существует ряд факторов, влияющих на восприятие человеком контраста. На данный момент в ветке GitHub много дискуссий.
ОПРЕДЕЛЕНИЕ СВЕТА
Яркость является спектрально-взвешенной, но в остальном линейной мерой света. Спектральное взвешивание основано на том, как трихроматическое зрение человека воспринимает световые волны различной длины. Это было частью измерений в экспериментах CIE 1931 года и результирующих цветовых пространств, таких как CIEXYZ (Яркость - это Y в XYZ).
В то время как XYZ является линейной моделью света, человеческое восприятие очень нелинейно. Таким образом, XYZ не является перцепционно однородным. Тем не менее, для ваших целей вы просто хотите узнать, какова эквивалентная яркость цвета по сравнению с серым пятном.
Предполагая, что вы начинаете с видео sRGB (т. Е. Стандартного цветового пространства в Интернете и на компьютере), вам сначала необходимо удалить гамма-кодирование, а затем применить спектральное взвешивание.
Я сделал много постов здесь, в стеке, о гамме, но если вы хотите получить четкое объяснение, я рекомендую Pamnton's Gamma FAQ.
Преобразование sRGB в линейный (гамма 1.0).
1) Преобразуйте значения R´G´B´ из 8-битного целого (0–255) в десятичное (0,0–1,0), разделив каждый канал отдельно на 255. Значения R´G´B´ должны быть от 0 до 1 для следуя математике, чтобы работать. Кроме того, вот ссылка на сообщение с фрагментом кода для преобразования одного числа (например, шестнадцатеричного шестнадцатеричного числа) в каналы RGB.
2) Линеаризовать каждый канал. Ленивый способ состоит в том, чтобы применить кривую мощности 2,2, как на мониторе компьютера отображаются данные изображения - для оценки яркости цвета это хорошо:
R´ ^ 2.2 = R lin G´ ^ 2.2 = G lin B´ ^ 2.2 = B lin
3) Альтернативный (более точный) метод: если вы выполняете обработку изображений и переходите от sRGB к линейному, то есть более точный метод, который есть в Википедии. Но также, вот фрагмент кода из моей электронной таблицы, который я использую для аналогичной цели:
=IF( A1 <= 0.04045 ; A1 / 12.92 ; POWER((( A1 + 0.055)/1.055) ; 2.4))
Это показывает, что для значений ниже 0,04045 вы просто делите на 12,92, но для значений выше вы смещаете и применяете степень 2,4 - обратите внимание, что "ленивым образом" мы использовали 2,2, но кривые практически идентичны из-за смещения / линеаризация.
Сделайте либо шаг 2, либо шаг 3, но не оба.
4) Наконец, примените коэффициенты для спектрального взвешивания и суммируйте три канала вместе:
R lin * 0,2126 + G lin * 0,7152 + B lin * 0,0722 = Y
И это дает вам Y, вашу яркость для данного цвета. Яркость также известна как L, но ее не следует путать с L* (Lstar), который представляет собой воспринимаемую яркость, а не яркость).
Определение воспринимаемого контраста
Теперь, если вы хотите определить разницу между двумя образцами, существует ряд методов. Контраст Вебера по существу составляет ΔL/L и является стандартом с 19-го века. Но для компьютерного монитора отображаются стимулы, я предлагаю несколько более современных подходов. Например, следующая модификация для лучшего восприятия результатов:
(L светлее - L темнее) / (L светлее + 0,1)
Есть также "Длина перцептивного контраста", Боуман-Саполинский и другие, включая некоторых, над которыми я работаю. Вы также можете конвертировать в CIELAB (L*a*b*)
который основан на человеческом восприятии, и вы просто вычитаете L * 1 из L * 2.
Кроме того, существует ряд других факторов, влияющих на восприятие контраста, таких как размер и вес шрифта, отступы (см. Эффекты объемного звучания Бартлсона-Бренемана) и другие факторы.
Пожалуйста, дай мне знать, если возникнут какие-либо вопросы.
Это мой обновленный код, основанный на том, что Myndex написал ранее.
В тестовом примере фиолетовый я использую hex #9d5fb0
(означает R: 157, G: 95, B: 176), а для зеленого я использую гекс #318261
(R = 49, G: 130, B: 97)
JS:
function HexToRGB(hex) {
// to allow shorthand input like #FFF or FFFFFF without # sign make it #FFFFFF
hex = String(hex);
if(hex.length==3){hex='#'+hex.substr(0, 1)+hex.substr(0, 1)+hex.substr(1, 1)+hex.substr(1, 1)+hex.substr(2, 1)+hex.substr(2, 1);}
if(hex.length==4){hex='#'+hex.substr(1, 1)+hex.substr(1, 1)+hex.substr(2, 1)+hex.substr(2, 1)+hex.substr(3, 1)+hex.substr(3, 1);}
if(hex.length==6){hex='#'+hex;}
let R = parseInt(hex.substr(1, 2),16);
let G = parseInt(hex.substr(3, 2),16);
let B = parseInt(hex.substr(5, 2),16);
console.log("rgb from "+hex+" = "+[R,G,B]);
return [R,G,B];
}
Программный усредненный метод для наиболее распространенных статей в оттенках серого:
СЕРЫЙ = круглый ((R + G + B) / 3);
JS:
function RGBToGRAY(rgb) {
let avg = parseInt((rgb[0]+rgb[1]+rgb[2])/3);
return [avg,avg,avg];
}
Это превратит фиолетовый в #8f8f8f, потому что среднее = 143
Это превратит зеленый в #5c5c5c, потому что в среднем = 92
Разница между 92 и 143 слишком велика и неверно пройдет мой ожидаемый тест. Симуляция Adobe преобразует те же примеры в оттенки серого, что и:
Hex # 777777 означает R:119, G:119, B:119.
Hex #747474 означает R:116, G:116, B:116.
Разница между 116 и 119, очевидно, невелика и должна не пройти мой ожидаемый тест на разницу. Таким образом, метод RGBToGRAY оказался неточным.
Теперь, как объяснил Myndex, мы должны сделать его линейным и применить гамма-коррекцию 2.2.
R´ ^ 2.2 = Рлин G´^2.2 = Глин B´^2.2 = Блин
JS:
function linearFromRGB(rgb) {
// make it decimal
let R = rgb[0]/255.0; // red channel decimal
let G = rgb[1]/255.0; // green channel decimal
let B = rgb[2]/255.0; // blue channel decimal
// apply gamma
let gamma = 2.2;
R = Math.pow(R, gamma); // linearize red
G = Math.pow(G, gamma); // linearize green
B = Math.pow(B, gamma); // linearize blue
let linear = [R,G,B];
console.log('linearized rgb = '+linear);
return linear;
}
Гамма-скорректированный линейный результат для фиолетового теперь R: 0,3440, G:0,1139, B:0,4423, а зеленый результат - R: 0,0265, G:0,2271, B:0,1192
Теперь получим легкость L или (Y по шкале XYZ), применяя коэффициенты:
Y = Rlin * 0,2126 + Glin * 0,7152 + Blin * 0,0722
JS
function luminanceFromLin(rgblin) {
let Y = (0.2126 * (rgblin[0])); // red channel
Y = Y + (0.7152 * (rgblin[1])); // green channel
Y = Y + (0.0722 * (rgblin[2])); // blue channel
console.log('luminance from linear = '+Y);
return Y;
}
Теперь воспринимается контраст между двумя значениями Y (или L):
(Llighter - Ldarker) / (Llighter + 0,1)
JS
function perceivedContrast(Y1,Y2){
let C = ((Math.max(Y1,Y2)-Math.min(Y1,Y2))/(Math.max(Y1,Y2)+0.1));
console.log('perceived contrast from '+Y1+','+Y2+' = '+C);
return C;
}
Теперь все вышеперечисленные функции объединены в один шаг в / вывод
function perceivedContrastFromHex(hex1,hex2){
let lin1 = linearFromRGB(HexToRGB(hex1));
let lin2 = linearFromRGB(HexToRGB(hex2));
let y1 = luminanceFromLin(lin1);
let y2 = luminanceFromLin(lin2);
return perceivedContrast(y1,y2);
}
и наконец тест
var P = perceivedContrastFromHex('#318261','#9d5fb0');
// compares the purple and green example
alert(P);
// shows 0.034369592139888626
var P = perceivedContrastFromHex('#000','#fff');
// compares pure black and white
alert(P);
// shows 0.9090909090909091
Просто для лучшего синтаксиса и простоты использования я поместил всю теорию в один анализатор внутри объекта, который работает следующим образом.
Парсер вычислит эти значения за один шаг от цвета 318261
:
Object:
hex: "#318261"
rgb: {r: 49, g: 130, b: 97}
int: 10313648
dec: {r: 0.19215686274509805, g: 0.5098039215686274, b: 0.3803921568627451}
lin: {r: 0.030713443732993635, g: 0.2232279573168085, b: 0.11953842798834562}
y: 0.17481298771137443
lstar: 48.86083783595441
Javascript может вызывать внутренний анализатор объекта с шестнадцатеричной строкой цвета в качестве параметра. Шестнадцатеричная строка может выглядеть как 000
или же #000
или же 000000
или же #000000
, Есть два способа обработки результата.
A: взять возвращенный объект как целое в переменную: var result = Color_Parser.parseHex('318261');
var lstar = result.lstar;
B: разбор и последующий доступ к частям последнего результата анализатора. Например, выберите только значение контраста L*:
Color_Parser.parseHex('#ABC');
var lstar = Color_Parser.result.lstar;
И вот полный код:
const Color_Parser = {
version : '1.0.0.beta',
name : 'Color_Parser',
result : null, // the parser output
loging : true, // set to false to disable writing each step to console log
parseHex : function(_input){
if(this.loging){console.log(this.name+', input: '+_input);}
this.result = {};
// pre flight checks
if(!_input){
this.result.error = true;
console.log(this.name+', error');
return this.result;
}
// first convert shorthand Hex strings to full strings
this.result.hex = String(_input);
if(this.result.hex.length==3){this.result.hex='#'+this.result.hex.substr(0, 1)+this.result.hex.substr(0, 1)+this.result.hex.substr(1, 1)+this.result.hex.substr(1, 1)+this.result.hex.substr(2, 1)+this.result.hex.substr(2, 1);}
if(this.result.hex.length==4){this.result.hex='#'+this.result.hex.substr(1, 1)+this.result.hex.substr(1, 1)+this.result.hex.substr(2, 1)+this.result.hex.substr(2, 1)+this.result.hex.substr(3, 1)+this.result.hex.substr(3, 1);}
if(this.result.hex.length==6){this.result.hex='#'+this.result.hex;}
if(this.loging){console.log(this.name+', added to result: '+this.result.hex);}
// second get values from the string segments as channels
this.result.rgb = {r:null,g:null,b:null};
this.result.rgb.r = parseInt(this.result.hex.substr(1, 2),16);
this.result.rgb.g = parseInt(this.result.hex.substr(3, 2),16);
this.result.rgb.b = parseInt(this.result.hex.substr(5, 2),16);
if(this.loging){console.log(this.name+', added to result: '+this.result.rgb);}
// third get the color value
this.result.int = ((this.result.rgb.r&0x0ff)<<16)|((this.result.rgb.g&0x0ff)<<8)|(this.result.rgb.b&0x0ff);
if(this.loging){console.log(this.name+', added to result: '+this.result.int);}
// fourth turn 8 bit channels to decimal
this.result.dec = {r:null,g:null,b:null};
this.result.dec.r = this.result.rgb.r/255.0; // red channel to decimal
this.result.dec.g = this.result.rgb.g/255.0; // green channel to decimal
this.result.dec.b = this.result.rgb.b/255.0; // blue channel to decimal
if(this.loging){console.log(this.name+', added to result: '+this.result.dec);}
// fifth linearize each channel
this.result.lin = {r:null,g:null,b:null};
for (var i = 0, len = 3; i < len; i++) {
if ( this.result.dec[['r','g','b'][i]] <= 0.04045 ) {
this.result.lin[['r','g','b'][i]] = this.result.dec[['r','g','b'][i]] / 12.92;
} else {
this.result.lin[['r','g','b'][i]] = Math.pow((( this.result.dec[['r','g','b'][i]] + 0.055)/1.055),2.4);
}
}
if(this.loging){console.log(this.name+', added to result: '+this.result.lin);}
// get Y from linear result
this.result.y = (0.2126 * (this.result.lin.r)); // red channel
this.result.y += (0.7152 * (this.result.lin.g)); // green channel
this.result.y += (0.0722 * (this.result.lin.b)); // blue channel
if(this.loging){console.log(this.name+', added to result: '+this.result.y);}
// get L* contrast from Y
if ( this.result.y <= (216/24389)) {
this.result.lstar = this.result.y * (24389/27);
} else {
this.result.lstar = Math.pow(this.result.y,(1/3)) * 116 - 16;
}
if(this.loging){console.log(this.name+', added to result: '+this.result.lstar);}
// compute grayscale is to be continued hereafter
// compute inverted rgb is to be continued hereafter
this.result.error = false;
if(this.loging){console.log(this.name+', final output:');}
if(this.loging){console.log(this.result);}
return this.result;
}
}
пожалуйста, прокомментируйте, если вы нашли ошибку в коде.
Может быть, это то, что может помочь. (Вытащено из пыльной старой склепы JS).
Я полагаю, что это было первоначально разработано, чтобы математически определить, является ли цвет текста на цветном фоне действительно читабельным.
Цветовой контраст
Определено (WCAG Версия 2)
http://www.w3.org/TR/2008/REC-WCAG20-20081211
Контрастность может варьироваться от 1 до 21
раздел 1.4.3
- Хорошо видимый: (улучшенный) Минимальный коэффициент контрастности от 7 до 1 - 7:1
- Обычный текст: минимальная контрастность от 4,5 до 1 - 4,5: 1
- Большой текст: минимальная контрастность от 3 до 1 - 3: 1
Эта функция ContraRatio выделяет число от 1 до 21, которое служит первым числом в соотношении.
например, n: 1, где "n" - результат этого метода
Чем выше число, тем оно более читабельно.
function getLum(rgb) {
var i, x;
var a = []; // so we don't mutate
for (i = 0; i < rgb.length; i++) {
x = rgb[i] / 255;
a[i] = x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
}
return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2];
}
var RE_HEX_RGB = /[a-f0-9]{6}|[a-f0-9]{3}/i;
function HEX_RGB(str) {
var match = str.toString(16).match(RE_HEX_RGB);
if (!match) {
return [0, 0, 0];
}
var colorString = match[0];
// Expand 3 character shorthand triplet e.g. #FFF -> #FFFFFF
if (match[0].length === 3) {
var Astr = colorString.split('');
for (var i = 0; i < Astr.length; i++) {
var ch = Astr[i];
Astr[i] = ch + ch;
}
colorString = Astr.join('');
}
var integer = parseInt(colorString, 16);
return [
(integer >> 16) & 0xFF,
(integer >> 8) & 0xFF,
integer & 0xFF
];
};
function contrastRatio(rgb1, rgb2) {
var l1 = getLum(rgb1);
var l2 = getLum(rgb2);
return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
}
var c1 = '#9d5fb0';
var c2 = '#318261';
var cr = contrastRatio( HEX_RGB(c1), HEX_RGB(c2) );
console.log("cr", cr);