RGB'yi RGBA'yı beyaza dönüştürün


196

Bir hex renk var, örneğin #F4F8FB(veya rgb(244, 248, 251)) mümkün olduğunca şeffaf bir rgba rengine (beyaz üzerinde görüntülendiğinde) dönüştürmek istiyorum. Mantıklı olmak? Bir algoritma arıyorum, ya da en azından bunu nasıl yapacağınız için bir algoritma fikri.

Örneğin:

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

Fikirler?


Guffa'nın cevabına dayanan FYI çözümü:

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)' 
    }

İnterseting! Hala görünür olmasını istiyorsun, tamamen şeffaf değil mi?
Mrchief

6
İlginç soru! Stackoverflow.com/questions/2049230/convert-rgba-color-to-rgb adresinde tam tersi bir soru var, yardımcı olabilir
apscience

@Mrchief - Evet, beyaz üzerinde görüntülendiğinde rengin aynı görünmesini istiyorum, ancak mümkün olduğunca şeffaf olmasını istiyorum.
Mark Kahn

1
Harika bir soru, her zaman bunu nasıl yapacağımı merak ettim, ancak genellikle ondan uzak durdum;) +1
Dominic K

1
@ e100 - Bunu tetikleyen özel soru stackoverflow.com/questions/6658350/… idi . Temelde, her şeyin üstüne bir gölge öğesi koyarak tıklamayı engellemeden arka plan rengiyle öğelerin üzerine CSS gölgeleri yerleştirmek istedim. Ben de bir süre yapmak istedim bu yüzden rastgele şeyler yapabilirsiniz: jsfiddle.net/qwySW/2 . Artı ben sadece büyüleyici bir soru olarak buldum :)
Mark Kahn

Yanıtlar:


185

En düşük renk bileşenini alın ve bunu alfa değerine dönüştürün. Ardından, en düşük değeri çıkararak ve alfa değerine bölerek renk bileşenlerini ölçeklendirin.

Misal:

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

Yani, olarak rgb(152, 177, 202)görüntülenir rgba(0, 62, 123, .404).

Photoshop'ta renklerin gerçekten mükemmel eşleştiğini doğruladım.


1
Teşekkür ederim! Ben benzer bir şey yapıyordum (örneğin olsun .404) ama sayıları çalışmak için oldukça alamadım.
Mark Kahn

3
FYI, söz konusu cevabınıza dayanarak nihai çözümü yayınladı
Mark Kahn

2
@templatetypedef: alfa te düşük bileşen dönüştürme aralığından gidilmesi 0..255için 0..1ve tersyüz. Kullanmak 1.0 - 152 / 255da işe yarar. Renk bileşenlerini dönüştürme basitçe gelen ölçekleme olduğu n..255için 0..255nerede nen düşük bileşenidir.
Guffa

1
@Christoph: İlke aynı, ama daha karmaşık olurdu. Alfa'yı hesaplamak için yalnızca en düşük bileşeni kullanmak yerine, her bileşen için mümkün olan en düşük alfa değerini (yani bileşen değeri olarak 0 veya 255'i kullanırken doğru rengi elde etmek için hangi alfa değerini) hesaplayacaksınız, ardından en yüksek alfa değerlerinin. Bundan sonra, doğru rengi elde etmek için bu alfa değerine sahip her bileşen için hangi renk değerini kullanacağınızı hesaplayabilirsiniz. Bazı renk kombinasyonlarının (örneğin, siyah zemin üzerine beyaz), kullanılacak en düşük alfa değeri olarak 1.0 vereceğini unutmayın.
Guffa

2
Bu çözümü uygulayan küçük bir keman yazdım. Bağlantı burada. jsfiddle.net/wb5fwLoc/1 . Belki biriniz kullanabilirsiniz. Sadece hızlı, hatasız bir komut dosyası değil .. oyun oynamak için yeterince iyi olmalı.
chsymann

23

R, g ve b'nin giriş değerleri ve r ', g', b 've a' nın hepsi 1 ve 0 arasında ölçeklenmiş (şimdilik matematik güzelliğini yaptığı gibi) çıkış değerleri olsun. kaplama renkleri için formül:

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

1 - a 'terimleri arka plan katkısını, diğer terimler ise ön planı temsil eder. Biraz cebir yap:

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

Sezgisel olarak, minimum renk değerinin problemdeki sınırlayıcı faktör olacağı anlaşılıyor, bu yüzden bunu m'ye bağlayın:

m = min(r, g, b)

Şeffaflığı en üst düzeye çıkarmak istediğimiz için, karşılık gelen çıkış değerini (m ') sıfıra ayarlayın:

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

Yani, javascript'te (yol boyunca 1'den 255'e çeviri):

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

Burada bir 'opaklık olduğunu varsayıyorum. Şeffaflık olarak değiştirmek önemsizdir - sadece "1 -" i bir 'formülünden kaldırın. Dikkat edilmesi gereken bir şey, bunun kesin sonuçlar üretmediği - yukarıda verdiğiniz örnek için opaklığın 0.498 olduğunu söyledi (128, 128, 255). Ancak, bu son derece yakındır.


Eğer [255 * (r/255 - 1) / a + 1 * 255, ...] == [(255*r/255 - 255 * 1) / a + 255, ...] == [(r - 255) / a + 255, ...]
çarptığınız

6

RGB <-> HSL dönüşümüne bakardım. Yani parlaklık == beyaz miktarı == şeffaflık miktarı.

Örneğin rgb( 128, 128, 255 ), RGB değerlerini 0önce maksimum miktara kaydırmamız gerekiyor , yani rgb( 0, 0, 128 )- olabildiğince az beyazla rengimiz olurdu. Bundan sonra, parlaklık için formül kullanarak, orijinal rengimizi elde etmek için koyu rengimize eklememiz gereken beyaz miktarını hesaplıyoruz - bu bizim alfamız olacak:

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;

Umarım mantıklıdır. :)


6

SASS / SCSS kullananlarınız için, @Guffa tarafından açıklanan algoritmayı kolayca kullanabilmeniz için küçük bir SCSS işlevi yazdım

@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

Sadece algoritma için bir fikir açıklıyorum, tam bir çözüm yok:

Temel olarak, üç numaraları var x, y, zve üç yeni numaralar Aradığınız x', y', z've bir çarpan aaralığında [0,1] böyle:

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

Bu, kanalların aynı zamanda [0,1] aralığında değer aldığı birimler halinde yazılır. 8 bit ayrık değerlerde, şöyle bir şey olurdu:

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

Dahası, mümkün olan en büyük değeri istiyorsunuz a. Çözebilirsiniz:

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

Vb Gerçek değerlerde bunun sonsuz sayıda çözümü vardır, sadece herhangi bir gerçek sayıyı takın a, ancak sorun, ayrıklaştırma hatasının minimum olduğu bir sayı bulmaktır.


0

Bunu yapmalı:

let x = min(r,g,b)
a = 1 - x/255                    # Correction 1
r,g,b = ( (r,g,b) - x ) / a      # Correction 2

Aslında alfa kireçte bir hata olduğunu fark ettim (şimdi düzeltildi). Yani bunun da onunla bir ilgisi olabilirdi.
doğruluk

İSS'niz tarafından tanıtılan bir sorun hala var yve (y-x). 255 / (y-x)Yanlış en büyük sayı zorlar 255her zaman tek bir ile bitirmek istiyorum böylece 0, tek 255ve sonra başka bir numaraya: 0, 22, 255, 255, 0, 55, vb ...
Mark Kahn

Görüyorum, bu yüzden daha koyu renklere izin vermiyor ... Sadece yapmalıyım /ave sonra doğru cevaba uyuyor.
doğruluk

-1

En iyi cevap düşük renk bileşenleri ile benim için işe yaramadı. Örneğin, renk # 80000 ise doğru alfa hesaplamaz. Teknik olarak alfa 0.5 ile # ff0000 haline getirmelidir. Bu sorunu çözmek için RGB -> HSL -> RGBA dönüşümünü kullanmanız gerekir. Doğru değerleri almak için sözde kod:

//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" yeni ayarlanan rengin değerini içerecek ve saydamlığı kontrol etmek için "alpha" değişkenini kullanacaktır.


#800000ve rgba( 255, 0, 0, .5 )hiçbir yerde aynı renge yakın değiller. Birincisi koyu kırmızı, ikinci somon balığı. Siyahın üzerinde göstermedikçe, sanırım aynı olurdu.
Mark Kahn
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.