Как инвертировать шестнадцатеричные значения RGB по контрасту в PHP


До сих пор у меня есть код ниже:

function hexrgb_invert($hex) {
    $arr = str_split($hex, 2);
    foreach ($arr as &$value) {
        $c = base_convert($value, 16, 10);
        $value = str_pad(base_convert(255 - $c, 10, 16), 2, '0', STR_PAD_LEFT);
    }
    return implode('', $arr);
}

Проблема: Мне нужно инвертировать цвета на основе контраста. Описанная выше функция работает для некоторых вещей, но не для других.

Пример: Если вход 9d702f, то выход будет 9d702f. (2 цвета с низкой контрастностью)

Мне не повезло искать что-либо еще в StackOverflow, так как большинство ответов, похоже, используют тот же алгоритм, который я уже использую.


Далее Примеры:

Предположим, что я пытаюсь найти контрастную противоположность #FFFFFF (белому). Это очень прямолинейно, потому что белый является основным цветом, поэтому его противоположность можно легко вычислить. (Для чего вышеуказанная функция будет отлично работать.) Противоположностью #FFFFFF, конечно, является #000000 (черный), и когда вы сравниваете 2 цвета, вы получаете коэффициент контрастности 21:1.

Однако, если мы попытаемся использовать ту же функцию выше для цвета #808080, это даст нам цвет #7F7F7F. Эти 2 цвета почти идентичны и имеют коэффициент контрастности всего 1,01:1. Это происходит потому, что чем ближе вы подходите к шестнадцатеричному 80 (десятичному 128), тем меньше контраста может обеспечить эта функция.

В конкретном случае #808080 цвет #000000 обеспечил бы наибольшее соответствие в 5.32:1.


Решение:

function rgb_best_contrast($r, $g, $b) {
    return array(
        'r' => ($r < 128) ? 255 : 0,
        'g' => ($g < 128) ? 255 : 0,
        'b' => ($b < 128) ? 255 : 0
    );
}
Author: Nicholas Summers, 2016-06-28

2 answers

str_pad добавляет символы справа по умолчанию - его необязательный аргумент $pad_type по умолчанию равен STR_PAD_RIGHT.

Вам нужно заставить его добавить нули слева:

str_pad(base_convert(255 - $c, 10, 16), 2, '0', STR_PAD_LEFT)

Ваш пример в деталях:

  • Ввод: 6ff060, принимая во внимание только значение G: $c = 0xf0 (десятичное число: 240).
  • 255 - 240 = 15 (шестнадцатеричный: f)
  • base_convert(255 - $c, 10, 16) производит: 'f' (в виде строки!)
  • str_pad(base_convert(255 - $c, 10, 16), 2, '0') добавляет один ноль справа , таким образом, производя 'f0'.
  • Настройка $pad_type = STR_PAD_LEFT устраняет проблему.
 2
Author: Alex Shesterov, 2016-06-27 21:41:51

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

Я использовал этот скрипт для поиска всего цветового пространства RGB в поисках наилучшего соответствия для любого заданного цвета:

$test_a = ['r' => 128, 'g' => 128, 'b' => 128];
$best = [
    'color' => ['r' => 128, 'g' => 128, 'b' => 128],
    'diff'  => 0.0
];
foreach (range(0, 255) as $r) {
    foreach (range(0, 255) as $g) {
        foreach (range(0, 255) as $b) {
            $test_b = ['r' => $r, 'g' => $g, 'b' => $b];
            // YQI sensitive contrast check
            $diff = check::rgb_contrast($test_a, $test_b);
            if ($diff > $best['diff']) {
                $best = [
                    'color' => $test_b,
                    'diff'  => $diff
                ];
            }
        }
    }
}
var_dump($best);

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

function rgb_best_contrast($r, $g, $b) {
    return array(
        'r' => ($r < 128) ? 255 : 0,
        'g' => ($g < 128) ? 255 : 0,
        'b' => ($b < 128) ? 255 : 0
    );
}

Работает точно так, как ожидалось. Всегда дает лучший контрастный цвет.

 0
Author: Nicholas Summers, 2016-06-29 20:04:27