Konvertieren Sie RGB in RGBA über Weiß

196

Ich habe eine Hex-Farbe, z. B. #F4F8FB(oder rgb(244, 248, 251)), die ich in eine möglichst transparente RGBA-Farbe umwandeln möchte (wenn sie über Weiß angezeigt wird). Sinn ergeben? Ich suche nach einem Algorithmus oder zumindest nach einer Idee, wie das geht.

Beispielsweise:

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

Ideen?


FYI-Lösung basierend auf Guffas Antwort:

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)' 
    }
Mark Kahn
quelle
Interseting! Sie möchten immer noch, dass es richtig sichtbar und nicht vollständig transparent ist?
Mrchief
6
Interessante Frage! Es gibt eine genau entgegengesetzte Frage unter stackoverflow.com/questions/2049230/convert-rgba-color-to-rgb , es könnte helfen
apscience
@Mrchief - Ja, ich möchte, dass die Farbe bei der Anzeige über Weiß gleich aussieht, aber so transparent wie möglich ist.
Mark Kahn
1
Tolle Frage, ich habe mich immer gefragt, wie ich das machen soll, habe mich aber normalerweise davor gescheut;) +1
Dominic K
1
@ e100 - Die spezifische Frage, die dies ausgelöst hat, war stackoverflow.com/questions/6658350/… . Grundsätzlich wollte ich in der Lage sein, CSS-Schatten über Elemente mit einer Hintergrundfarbe zu legen, ohne den Klick zu blockieren, indem ich ein Schattenelement über alles legte. Ich wollte das auch schon eine Weile machen, damit ich zufällige Dinge machen kann wie: jsfiddle.net/qwySW/2 . Außerdem fand ich es gerade eine faszinierende Frage :)
Mark Kahn

Antworten:

185

Nehmen Sie die niedrigste Farbkomponente und konvertieren Sie diese in einen Alpha-Wert. Skalieren Sie dann die Farbkomponenten, indem Sie die niedrigsten subtrahieren und durch den Alpha-Wert dividieren.

Beispiel:

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

Also, rgb(152, 177, 202)zeigt als rgba(0, 62, 123, .404).

Ich habe in Photoshop überprüft, ob die Farben tatsächlich perfekt übereinstimmen.

Guffa
quelle
1
Danke dir! Ich habe etwas Ähnliches gemacht (zB get .404), konnte aber die Zahlen nicht ganz zum Laufen bringen.
Mark Kahn
3
Zu Ihrer Information, veröffentlichte endgültige Lösung basierend auf Ihrer Antwort in Frage
Mark Kahn
2
@templatetypedef: Das Konvertieren der niedrigsten Komponente in Alpha wird vom Bereich 0..255in skaliert 0..1und invertiert. Verwenden 1.0 - 152 / 255würde auch funktionieren. Die Farbkomponenten Umwandlung skaliert einfach aus n..255zu , 0..255wo ndie niedrigste Komponente.
Guffa
1
@Christoph: Das Prinzip wäre das gleiche, aber komplizierter. Anstatt nur die niedrigste Komponente zur Berechnung des Alphas zu verwenden, würden Sie für jede Komponente das niedrigstmögliche Alpha berechnen (dh welchen Alpha-Wert Sie verwenden müssen, um die richtige Farbe zu erhalten, wenn Sie entweder 0 oder 255 als Komponentenwert verwenden) und dann den höchsten Wert verwenden dieser Alpha-Werte. Daraus können Sie berechnen, welcher Farbwert für jede Komponente mit diesem Alpha-Wert verwendet werden soll, um die richtige Farbe zu erhalten. Beachten Sie, dass einige Farbkombinationen (z. B. Weiß auf schwarzem Hintergrund) 1,0 als niedrigstmöglichen Alpha-Wert ergeben.
Guffa
2
Ich habe eine kleine Geige geschrieben, die diese Lösung implementiert. Hier ist der Link. jsfiddle.net/wb5fwLoc/1 . Vielleicht kann einer von euch es benutzen. Es ist nur ein schnelles, nicht fehlerfreies Skript. Es sollte gut genug sein, um herumzuspielen.
Chsymann
23

Sei r, g und b die Eingabewerte und r ', g', b 'und a' die Ausgabewerte, alle skaliert (vorerst, da dies die Mathematik schöner macht) zwischen 1 und 0. Dann durch die Formel zum Überlagern von Farben:

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

Die Begriffe 1 - a 'stellen den Hintergrundbeitrag dar, und die anderen Begriffe stellen den Vordergrund dar. Mach etwas Algebra:

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

Intuitiv scheint der minimale Farbwert der begrenzende Faktor für das Problem zu sein. Binden Sie ihn also an m:

m = min(r, g, b)

Setzen Sie den entsprechenden Ausgabewert m 'auf Null, um die Transparenz zu maximieren:

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

Also, in Javascript (übersetzt von 1 bis 255 auf dem Weg):

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];
}

Beachten Sie, dass ich davon ausgehe, dass a 'hier Deckkraft ist. Es ist trivial, es in Transparenz zu ändern - entfernen Sie einfach die "1 -" aus der Formel für a '. Zu beachten ist, dass dies keine exakten Ergebnisse zu liefern scheint - es wurde angegeben, dass die Opazität für das oben angegebene Beispiel 0,498 betrug (128, 128, 255). Dies ist jedoch sehr nahe.

gereeter
quelle
Wenn Sie die multiplizierten 255 in Ihrer Gleichung verteilen, erhalten Sie das richtige Ergebnis -[255 * (r/255 - 1) / a + 1 * 255, ...] == [(255*r/255 - 255 * 1) / a + 255, ...] == [(r - 255) / a + 255, ...]
gereeter
6

Ich würde auf RGB <-> HSL-Konvertierung schauen. Dh Leuchtkraft == Menge Weiß == Menge Transparenz.

In Ihrem Beispiel rgb( 128, 128, 255 )müssen wir die RGB-Werte 0zuerst um den maximalen Betrag verschieben, dh um rgb( 0, 0, 128 )- das wäre unsere Farbe mit so wenig Weiß wie möglich. Und danach berechnen wir mithilfe der Formel für die Luminanz die Menge an Weiß, die wir zu unserer dunklen Farbe hinzufügen müssen, um die ursprüngliche Farbe zu erhalten - das wäre unser Alpha:

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;

Hoffe das macht Sinn. :) :)

lxa
quelle
6

Für diejenigen unter Ihnen, die SASS / SCSS verwenden, habe ich eine kleine SCSS-Funktion geschrieben, damit Sie den von @Guffa beschriebenen Algorithmus problemlos verwenden können

@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
    );
}
Stefan
quelle
3

Ich beschreibe nur eine Idee für den Algorithmus, keine vollständige Lösung:

Grundsätzlich haben Sie drei Zahlen x, y, zund Sie suchen drei neue Nummern x', y', z'und ein Multiplikator aim Bereich von [0,1] , so dass:

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

Dies wird in Einheiten geschrieben, in denen die Kanäle auch Werte im Bereich [0,1] annehmen. Bei diskreten 8-Bit-Werten wäre dies ungefähr so:

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

Darüber hinaus möchten Sie den größtmöglichen Wert a. Sie können lösen:

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

Usw. In reellen Werten hat dies unendlich viele Lösungen. Stecken Sie einfach eine beliebige reelle Zahl ein a, aber das Problem besteht darin, eine Zahl zu finden, für die der Diskretisierungsfehler minimal ist.

Kerrek SB
quelle
0

Dies sollte es tun:

let x = min(r,g,b)
a = 1 - x/255                    # Correction 1
r,g,b = ( (r,g,b) - x ) / a      # Correction 2
Wahrhaftigkeit
quelle
Mir ist gerade aufgefallen, dass ich einen Fehler in der Alpha-Berechnung hatte (jetzt behoben). Das könnte also auch etwas damit zu tun haben.
Wahrhaftigkeit
Es gibt immer noch ein Problem, das von Ihrem yund eingeführt wurde (y-x). Das 255 / (y-x)zwingt falsch die größte Zahl an 255, so dass Sie immer mit einem einzigen Ende würde 0, eine einzige 255und dann eine andere Nummer: 0, 22, 255, 255, 0, 55usw ...
Mark Kahn
Ich verstehe, es werden also keine dunkleren Farben zugelassen ... Ich sollte es einfach machen /aund dann stimmt es mit der richtigen Antwort überein.
Wahrhaftigkeit
-1

Die Top-Antwort hat bei niedrigen Farbkomponenten bei mir nicht funktioniert. Beispielsweise wird das korrekte Alpha nicht berechnet, wenn die Farbe # 80000 ist. Technisch sollte es mit alpha 0.5 in # ff0000 kommen. Um dies zu beheben, müssen Sie RGB -> HSL -> RGBA-Konvertierung verwenden. Dies ist Pseudocode, um die richtigen Werte zu erhalten:

//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" enthält den Wert der neu eingestellten Farbe und verwendet die Variable "alpha", um die Transparenz zu steuern.

Arvydas Juskevicius
quelle
#800000und rgba( 255, 0, 0, .5 )sind nicht annähernd die gleiche Farbe. Der erste wäre dunkelrot, der zweite lachsfarben. Wenn sie nicht über Schwarz angezeigt werden, sind sie wahrscheinlich gleich.
Mark Kahn