Как эффективно рассчитать разницу контрастности человеческого глаза для значений RGB? - PullRequest
3 голосов
/ 18 мая 2019

Чтобы проверить, будут ли два цвета в оттенках серого слишком близко, чтобы их различил человеческий глаз.Я хочу иметь возможность генерировать предупреждение пользователю, если выбраны «опасные» цвета.Следовательно, основываясь на результате, мы можем решить, следует ли для людей с плохим зрением изменить один из двух цветов на белый или черный, чтобы улучшить читаемый контраст.Например, шестнадцатеричные цвета #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 ]

3 голосов
/ 18 мая 2019

КОНТРАСТ И ВЗГЛЯД СВЕТА

То, что вы ищете, это как оценить Контрастность яркости.

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

И просто к вашему сведению термин «яркость», а не «яркость». Светимость относится к свету, испускаемому со временем , часто используемому в астрономии. Когда мы говорим о колориметрии, мы используем термин «яркость», другой показатель освещенности, определенный CIEXYZ (CIE 1931).

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

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

ОПРЕДЕЛЕНИЕ СВЕТА

Яркость - это спектрально-взвешенная, но в остальном линейная мера света. Спектральное взвешивание основано на том, как трихроматическое зрение человека воспринимает световые волны различной длины. Это было частью измерений в экспериментах CIE 1931 года и результирующих цветовых пространств, таких как CIEXYZ (Яркость - это Y в XYZ).

Хотя XYZ - это линейная модель света, человеческое восприятие очень нелинейно. Таким образом, XYZ не является перцепционно однородным. Тем не менее, для ваших целей вы просто хотите узнать, какова эквивалентная яркость цвета по сравнению с серым пятном.

Предполагая, что вы начинаете с видео sRGB (т. Е. Стандартного цветового пространства в Интернете и на компьютере), вам сначала необходимо удалить гамма-кодирование, а затем применить спектральное взвешивание.

Я сделал много постов здесь, в стеке, о гамме, но если вы хотите получить однозначное объяснение, я рекомендую Часто задаваемые вопросы по Гамме Пойнтона.

Преобразование 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) ALTERNATE (более точный) метод: если вы выполняете обработку изображений и переходите от sRGB к линейному, то есть более точный метод, который в * wikipedia . Кроме того, вот фрагмент кода из моей электронной таблицы, который я использую для аналогичных целей:

  =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 Лин * 0,2126 + Г Лин * 0,7152 + В Лин * 0,0722 = Y

И это дает вам Y, вашу яркость для данного цвета. Яркость, также известную как L , но ее не следует путать с L * (Lstar), который является воспринимаемой легкостью, а не яркости).

Определение воспринимаемого контраста

Теперь, если выВы хотите определить разницу между двумя образцами, существует ряд методов. Контраст Вебера по существу составляет ΔL / L и является стандартом с 19-го века. Но для компьютерного монитора отображаются стимулы, я предлагаю несколько более современных подходов. Например, следующая модификация для лучшего восприятия результатов:

(L светлее - L темнее ) / (L светлее + 0,1)

Существует также «Длина воспринимаемого контраста», Боуман-Саполинский и другие, включая некоторых, над которыми я работаю. Вы также можете конвертировать в CIELAB (L*a*b*), который основан на человеческом восприятии, и там вы просто вычитаете L * 1 из L * 2 .

Кроме того, существует ряд других факторов, влияющих на восприятие контраста, таких как размер и вес шрифта, отступы (см. Эффекты объемного звучания Бартлсона-Бренемана) и другие факторы.

Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы.

2 голосов
/ 21 мая 2019

ОТВЕТ НА СЛЕДУЮЩИЙ

Я публикую это в качестве дополнительного ответа, чтобы не только уточнить мой первоначальный ответ (который я также только что отредактировал), но также добавить фрагменты кода различных концепций.Каждый шаг в процессе от 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.

STEP ONE (8-битный в десятичный)

Преобразование 8-битных значений sRGBв десятичное число путем деления на 255:

десятичное = 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

STEP TWO (линеаризация, точная версия)

Используйте эту версиювместо простой версии 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
Другая альтернатива, которая мне нравится больше, чем другие, - преобразование линеаризованной яркости (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 Нет Bueno!

OP 2x2p упомянул широко цитируемое уравнение для создания оттенков серого цвета в виде:

СЕРЫЙ = круглый ((R + G + B) / 3);

Он указал, насколько неточно это выглядело, и действительно - это совершенно неправильно .Спектральные веса R, G и B являются существенными и не могут быть пропущены.ЗЕЛЕНЫЙ - это более высокая яркость, чем СИНИЙ на ПОРЯДОК МАГНИТНОСТИ. Вы не можете просто суммировать все три канала вместе, разделить на три и получить что-нибудь близкое к фактической яркости определенного цвета.

Я полагаю, что путаница по этому поводу могла возникнуть из-за управления цветом, известного как HSI (оттенок, насыщенность, интенсивность) .Но этот контроль не является (и никогда не должен быть) перцепционно однородным !!!HSI, как и HSV, - это просто «удобство» для манипулирования значениями цвета в компьютере.Они не одинаковы по восприятию, и математика, которую они используют, предназначена исключительно для поддержки «простого» способа настройки значений цвета в программном обеспечении.

Образцы цветов OP

2x2p опубликовал свой код, используя '# 318261', '# 9d5fb0' в качестве тестовых цветов.Вот как они выглядят в моей электронной таблице вместе с каждым значением на каждом этапе процесса преобразования (используя «точный» метод sRGB):

enter image description here

Оба близки к среднему серому # 777777.Обратите также внимание на то, что в то время как яркость L составляет всего 18, воспринимаемая яркость L * составляет 50.

0 голосов
/ 31 мая 2019

Просто для лучшего синтаксиса и простоты использования я поместил всю теорию в один анализатор внутри объекта, который работает следующим образом.

Анализатор будет вычислять эти значения за один шаг из цвета 318261:

Возвращаемый объект будет выглядеть следующим образом:

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 int 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 combined color int 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 color
    this.result.invert = {
      r: null,
      g: null,
      b: null,
      hex: null
    };
    this.result.invert.r = (255 - this.result.rgb.r);
    this.result.invert.g = (255 - this.result.rgb.g);
    this.result.invert.b = (255 - this.result.rgb.b);
    // reverse compute hex from inverted rgb          
    this.result.invert.hex = this.result.invert.b.toString(16); // begin with blue channel
    if (this.result.invert.hex.length < 2) {
      this.result.invert.hex = '0' + this.result.invert.hex;
    }
    this.result.invert.hex = this.result.invert.g.toString(16) + this.result.invert.hex;
    if (this.result.invert.hex.length < 4) {
      this.result.invert.hex = '0' + this.result.invert.hex;
    }
    this.result.invert.hex = this.result.invert.r.toString(16) + this.result.invert.hex;
    if (this.result.invert.hex.length < 6) {
      this.result.invert.hex = '0' + this.result.invert.hex;
    }
    this.result.invert.hex = '#' + this.result.invert.hex;
    this.result.error = false;
    if (this.loging) {
      console.log(this.name + ', final output:');
    }
    if (this.loging) {
      console.log(this.result);
    }
    return this.result;
  }
}
0 голосов
/ 21 мая 2019

Это мой обновленный код, основанный на том, что Myndex написал ранее.

В качестве тестового примера фиолетовый я использую hex #9d5fb0 (расшифровывается как R: 157, G: 95, B: 176 ) и для зеленого я использую hex #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 преобразует те же примеры в оттенки серого, что и:

Шестнадцатеричный # 777777, обозначающий R: 119, G: 119, B: 119

Шестнадцатеричный # 747474, обозначающий R: 116, G: 116, B: 116

Разница между 116 и 119, очевидно, невелика и должна провалиться в моем тесте ожидаемой разницы.Таким образом, метод RGBToGRAY, таким образом, оказался неточным.

Теперь, как объяснено Myndex, мы должны сделать его линейным и применить коррекцию гаммы 2.2.

R´ ^ 2.2 = Rlin 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
0 голосов
/ 19 мая 2019

Может быть, это то, что может помочь.(Вытащено из пыльного олд js crypt).

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

Цветовой контраст

Определеноby (WCAG Version 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

Эта функция контрастное отношение выделяет число от 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);
...