Конвертировать RGB в RGBA на белый

196

У меня есть шестнадцатеричный цвет, например #F4F8FB(или rgb(244, 248, 251)), который я хочу преобразовать в максимально прозрачный как возможный цвет rgba (при отображении над белым). Есть смысл? Я ищу алгоритм или, по крайней мере, идею алгоритма, как это сделать.

Например:

rgb( 128, 128, 255 ) --> rgba(   0,   0, 255,  .5 )
rgb( 152, 177, 202 ) --> rgba(  50, 100, 150,  .5 ) // can be better(lower alpha)

Идеи?


К вашему сведению решение, основанное на ответе Гуффы:

function RGBtoRGBA(r, g, b){
    if((g == null) && (typeof r === 'string')){
        var hex = r.replace(/^\s*#|\s*$/g, '');
        if(hex.length === 3){
            hex = hex.replace(/(.)/g, '$1$1');
        }
        r = parseInt(hex.substr(0, 2), 16);
        g = parseInt(hex.substr(2, 2), 16);
        b = parseInt(hex.substr(4, 2), 16);
    }

    var min, a = (255 - (min = Math.min(r, g, b))) / 255;

    return {
        r    : r = 0|(r - min) / a,
        g    : g = 0|(g - min) / a,
        b    : b = 0|(b - min) / a,
        a    : a = (0|1000*a)/1000,
        rgba : 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')'
    };
}

RGBtoRGBA(204, 153, 102) == RGBtoRGBA('#CC9966') == RGBtoRGBA('C96') == 
    {
       r    : 170,
       g    : 85 ,
       b    : 0  ,
       a    : 0.6,
       rgba : 'rgba(170, 85, 0, 0.6)' 
    }
Марк Кан
источник
Interseting! Вы все еще хотите, чтобы он был видимым, а не полностью прозрачным?
Mrchief
6
Интересный вопрос! На stackoverflow.com/questions/2049230/convert-rgba-color-to-rgb есть совершенно противоположный вопрос , это может помочь
apscience
@Mrchief - Да, я хочу, чтобы цвет выглядел одинаково при отображении на белом фоне, но был максимально прозрачным.
Марк Кан
1
Отличный вопрос, я всегда задавался вопросом, как это сделать, но обычно уклонялся от этого;) +1
Доминик К,
1
@ e100 - конкретный вопрос, который вызвал это stackoverflow.com/questions/6658350/… . По сути, я хотел иметь возможность накладывать тени CSS поверх элементов на цвет фона, не блокируя переходы, помещая элемент тени поверх всего. Я также хотел сделать это некоторое время, чтобы я мог делать случайные вещи, такие как: jsfiddle.net/qwySW/2 . Плюс я только что нашел, что это был захватывающий вопрос :)
Марк Кан

Ответы:

185

Возьмите самый низкий цветовой компонент и преобразуйте его в альфа-значение. Затем масштабируйте цветовые компоненты, вычитая самый низкий и деля на альфа-значение.

Пример:

152 converts to an alpha value of (255 - 152) / 255 ~ 0.404

152 scales using (152 - 152) / 0.404 = 0
177 scales using (177 - 152) / 0.404 ~ 62
202 scales using (202 - 152) / 0.404 ~ 123

Итак, rgb(152, 177, 202)отображается как rgba(0, 62, 123, .404).

В Photoshop я проверил, что цвета на самом деле идеально совпадают.

Guffa
источник
1
Спасибо! Я делал что-то подобное (например, получить .404), но не мог заставить цифры работать.
Марк Кан
3
К вашему сведению, опубликовано окончательное решение на основе вашего ответа на вопрос
Марк Кан
2
@templatetypedef: преобразование низшего компонента в альфа масштабируется от диапазона 0..255до 0..1и инвертируется. Использование 1.0 - 152 / 255также будет работать. Преобразование цветовых компонентов просто масштабирование от n..255до , 0..255где nсамый низкий компонент.
Гуффа
1
@Christoph: принцип будет таким же, но более сложным. Вместо того, чтобы просто использовать самый низкий компонент для вычисления альфа, вы должны рассчитать самый низкий из возможных альфа для каждого компонента (то есть, какое значение альфа использовать, чтобы получить правильный цвет при использовании 0 или 255 в качестве значения компонента), а затем использовать самое высокое из этих альфа-значений. Из этого вы можете рассчитать, какое значение цвета использовать для каждого компонента с этим альфа-значением, чтобы получить правильный цвет. Обратите внимание, что некоторые цветовые комбинации (например, белый на черном фоне) будут давать 1,0 как минимально возможное альфа-значение для использования.
Гуффа
2
Я написал небольшую скрипку, которая реализует это решение. Вот ссылка. jsfiddle.net/wb5fwLoc/1 . Может быть, один из вас может использовать это. Это просто быстрый, не содержащий ошибок скрипт ... он должен быть достаточно хорош для тренировки.
Чсиманн
23

Пусть r, g и b будут входными значениями, а r ', g', b 'и a' будут выходными значениями, все масштабированы (на данный момент, поскольку это делает математику более симпатичной) между 1 и 0. Затем формула для наложения цветов:

r = a' * r' + 1 - a'
g = a' * g' + 1 - a'
b = a' * b' + 1 - a'

Термины 1 - a представляют фоновый вклад, а остальные - передний план. Делаем некоторую алгебру:

r = a' * (r' - 1) + 1
r - 1 = a' * (r' - 1)
(r - 1) / (r' - 1) = a'
(r' - 1) / (r - 1) = 1 / a'
r' - 1 = (r - 1) / a'
r' = (r - 1) / a' + 1

Интуитивно кажется, что минимальное значение цвета будет ограничивающим фактором в проблеме, поэтому свяжите это с m:

m = min(r, g, b)

Установите соответствующее выходное значение m 'в ноль, так как мы хотим максимизировать прозрачность:

0 = (m - 1) / a' + 1
-1 = (m - 1) / a'
-a' = m - 1
a' = 1 - m

Итак, в javascript (перевод с 1 на 255 по пути):

function rgba(r, g, b) {
    var a = 1 - Math.min(r, Math.min(g, b)) / 255;
    return [255 + (r - 255) / a, 255 + (g - 255) / a, 255 + (b - 255) / a, a];
}

Обратите внимание, что я предполагаю, что а 'здесь непрозрачность. Тривиально изменить его на прозрачность - просто удалите «1» из формулы для «. Ничего не стоит отметить, что это, похоже, не дает точных результатов - в нем говорится, что непрозрачность была 0,498 для приведенного выше примера (128, 128, 255). Тем не менее, это очень близко.

gereeter
источник
Если вы распределите умноженное 255 в вашем уравнении, вы получите правильный результат -[255 * (r/255 - 1) / a + 1 * 255, ...] == [(255*r/255 - 255 * 1) / a + 255, ...] == [(r - 255) / a + 255, ...]
gereeter
6

Я бы посмотрел на преобразование RGB <-> HSL. Т.е. светимость == количество белого == количество прозрачности.

Для вашего примера rgb( 128, 128, 255 )нам нужно сместить значения RGB в 0первую очередь на максимальную величину, т. Е. Чтобы rgb( 0, 0, 128 )- это был бы наш цвет с как можно меньшим количеством белого. И после этого, используя формулу для яркости, мы вычисляем количество белого, которое нам нужно добавить к нашему темному цвету, чтобы получить оригинальный цвет - это будет наша альфа:

L = (MAX(R,G,B) + MIN(R,G,B))/2
L1 = (255 + 128) / 2 = 191.5
L2 = (128 + 0) /2 = 64
A = (191,5 - 64) / 255 = 0,5;

Надеюсь, что это имеет смысл. :)

LXA
источник
6

Для тех из вас, кто использует SASS / SCSS, я написал небольшую функцию SCSS, чтобы вы могли легко использовать алгоритм, описанный @Guffa

@function transparentize-on-white($color)
{
    $red: red($color);
    $green: green($color);
    $blue: blue($color);
    $lowestColorComponent: min($red, $green, $blue);
    $alpha: (255 - $lowestColorComponent) / 255;

    @return rgba(
        ($red - $lowestColorComponent) / $alpha,
        ($green - $lowestColorComponent) / $alpha,
        ($blue - $lowestColorComponent) / $alpha,
        $alpha
    );
}
Стефан
источник
3

Я просто описываю идею для алгоритма, а не полное решение:

В принципе, у вас есть три номера x, y, zи вы ищете три новые номера x', y', z'и умножитель aв диапазоне [0,1], что:

x = a + (1 - a) x'
y = a + (1 - a) y'
z = a + (1 - a) z'

Это записывается в единицах, где каналы также принимают значения в диапазоне [0,1]. В 8-битных дискретных значениях это будет примерно так:

x = 255 a + (1 - a) x'
y = 255 a + (1 - a) y'
z = 255 a + (1 - a) z'

Более того, вы хотите максимально возможное значение a. Вы можете решить:

a  = (x - x')/(255 - x')          x' = (x - 255 a)/(1 - a)

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

Керрек С.Б.
источник
0

Это должно сделать это:

let x = min(r,g,b)
a = 1 - x/255                    # Correction 1
r,g,b = ( (r,g,b) - x ) / a      # Correction 2
trutheality
источник
Я на самом деле только что заметил, что у меня была ошибка в альфа-калькуляции (исправлено сейчас) Так что, возможно, это как-то связано с этим.
Истина
Там все еще проблема, представленная вашим yи (y-x). 255 / (y-x)Неправильно заставляет наибольшее число к 255, так что вы всегда в конечном итоге с одним 0, один , 255а затем другой номер: 0, 22, 255, 255, 0, 55и т.д ...
Марк Кан
Я вижу, поэтому он не учитывает более темные цвета ... Я должен просто сделать это, /aи тогда он соответствует правильному ответу.
Истина
-1

Верхний ответ не работал для меня с низкоцветными компонентами. Например, он не вычисляет правильную альфу, если цвет # 80000. Технически это должно быть в # ff0000 с альфа 0.5. Чтобы решить эту проблему, вам нужно использовать RGB -> HSL -> RGBA-преобразование. Это псевдокод для получения правильных значений:

//Convert RGB to HSL
hsl = new HSL(rgb)

//Use lightness as alpha
alpha = hsl.Lightness

//For #80000 lightness is 0.5, so we have to take this into account.
//Lightness more than 0.5 converts to alpha 1 and below is a linear ratio
if (alpha > 0.5)
{
    alpha = 1;
}
else
{
    alpha = alpha / 0.5;
    //We need to drop the lightness of the color to 0.5 to get the actual color
    //that needs to display. Alpha blending will take care of that.
    hsl.Lightness = 0.5;
}

newRgb = hsl.convertToRgb()

«newRgb» будет содержать значение нового настроенного цвета и использовать переменную «alpha» для управления прозрачностью.

Арвидас Юскевичус
источник
#800000и rgba( 255, 0, 0, .5 )далеко не одного цвета. Первый будет темно-красным, второй лососевый. Если не показывать на черном, то я думаю, что они будут такими же.
Марк Кан