Valores HSL para RGB

17

O principal objetivo do modelo de cores RGB (vermelho verde azul) é detectar, representar e exibir imagens em sistemas eletrônicos, como televisores e computadores

HSL (Hue Saturation Lightness) é um modelo de cor alternativo, projetado na década de 1970 por pesquisadores de computação gráfica para se alinhar mais de perto com a maneira como a visão humana percebe atributos de produção de cores

Aqui estão os artigos do wiki para RGB e HSL . É comum que programas gráficos façam os cálculos em HSL e depois convertam para o formato preferido para a maioria das telas: RGB.

A tarefa é escrever uma função / programa que aceite HSL como entrada e produza RGB.

Você pode escolher sua representação preferida para E / S, desde que consistente entre elas.

Por exemplo, eles podem ser um array / tupla com 3 elementos ou um objeto com 3 propriedades nomeadas h, se l, mas eu vou aceitar outras variações inteligentes, como receber o HSL como um inteiro (precisão perder) e produzir um inteiro rgb.

A entrada pode ser considerada segura no intervalo e no formato, os quais você pode decidir. Eu sugiro fortemente os intervalos 0-1 0-1 0-1ou 0-360 0-100 0-100para hsl e / 0-1 0-1 0-1ou 0-255 0-255 0-255rgb.

Cada resposta é necessária para especificar as duas opções acima e coloque várias variações em suas respostas se você estiver particularmente orgulhoso delas, mesmo que elas não tenham menos caracteres que suas outras variações. Coloque o menor por cima.

Casos de pseudo teste para 0-360 0-100 0-1000-255 0-255 0-255

h   s   l   → r   g   b

0   0   0   → 0   0   0
90  56  17  → 43  68  19
202 19  39  → 81  104 118
72  55  26  → 88  103 30

As fórmulas para a conversão podem ser encontradas aqui :

Essa é uma boa maneira de visualizar a conversão:

towc
fonte
Seu intervalo sugerido para Hof 0-360é [0,360): seria melhor escrito como 0-359?
Jonathan Allan
3
@ JonathanAllan Eu acho que pode parecer um pouco mais confuso, quanto a mim, que sugere que 395.1 não é uma entrada possível. Se alguma coisa, usar a a-bnotação é errada por si só ao lidar com valores não inteiros, mas eu diria que não há problema em manter a pergunta mais legível. Se alguém reclamar, eu vou repensar, então obrigado por apontar isso
towc
Sim, concordou com o ponto de 359,1 - talvez apenas usar a notação padrão de [0,360)então :)
Jonathan Allan
surpreso ao não encontrar respostas glsl;)
towc

Respostas:

8

JavaScript (ES6), 98 95 94 bytes

Toma H em [0,360) e S / L em [0,1) . Emite R , G e B como uma matriz de 3 flutuadores em [0,1) .

(H,S,L)=>[5,3,1].map(i=>A(L*2)*S*([1,Y,0,0,Y,1][(i-~H)%6]-.5)+L,Y=(A=n=>n>1?2-n:n)((H/=60)%2))

Casos de teste

Esse snippet converte os resultados novamente em [0,255] .

Quão?

Código de inicialização

Y = (                  // we compute Y = 1 - |(H/60) mod 2 - 1| = X / C
  A = n =>             // A = function that returns 1 - |n - 1|
    n > 1 ?            //   first compare n with 1
      2 - n            //     which allows to simplify the formula to either 2 - n
    :                  //   or
      n                //     just n
)((H /= 60) % 2)       // divide H by 60 and compute Y = A(H % 2)

Código principal

[5, 3, 1].map(i =>     // for each i in (5, 3, 1):
  A(L * 2) * S * (     //   compute (1 - |2L - 1|) * S (this is C), and multiply it by:
    [1, Y, 0, 0, Y, 1] //     either 0, 1 or Y (let's call this factor K), picked from
    [(i - ~H) % 6]     //     a cyclic sequence of period 6, using i and ~H (-6 ≤ ~H ≤ -1)
    - .5               //     minus 1/2
  )                    //   this gives: C(K - 1/2) = CK - C/2, where CK = 0, C or X
  + L                  //   we add L, leading to CK - C/2 + L = CK + m
)                      // end of map() --> returns [R, G, B]

Permutações de (0, C, X)

A parte um pouco complicada é gerar a permutação correta de (0, C, X) de acordo com o ângulo do componente de matiz. Conforme mostrado na figura a seguir, cada valor da coluna é selecionado na mesma sequência de ciclo do período 6 , iniciando com diferentes compensações. No código acima, estamos usando - ~ H em vez de apenas + H porque precisamos coagir H a um número inteiro. Daí as compensações (5,3,1) em vez de (0,4,2) .

                       C,X,0,0,X,C,C,X,0,0,X,C, ...
 +------> C,X,0,0,X,C  <--------->                  offset = 0, (0 - 1) mod 6 = 5
 | +----> X,C,C,X,0,0          <--------->          offset = 4, (4 - 1) mod 6 = 3
 | | +--> 0,0,X,C,C,X      <--------->              offset = 2, (2 - 1) mod 6 = 1
 | | |
(C,X,0) for   0 ≤ H <  60
(X,C,0) for  60 ≤ H < 120
(0,C,X) for 120 ≤ H < 180
(0,X,C) for 180 ≤ H < 240
(X,0,C) for 240 ≤ H < 300
(C,0,X) for 300 ≤ H < 360
Arnauld
fonte
Será que tomar Hem [0,6)e saída em [0,1]salvar alguns bytes?
Jonathan Allan
@ JonathanAllan Ah, eu não percebi que o formato de E / S estava solto. Obrigado!
Arnauld
4

Mathematica, 155 bytes

(x~Clear~c;d=Piecewise@Table[{(P=Permutations)[P@{c,x,0}][[42,i]],(i-1)<=#<i*p},{i,6}];c=(1-Abs[2#3-1])#2;x=c(1-Abs[Mod[#/(p=60),2]-1]);m=#3-c/2;(m+d)255)&


Experimente online!

J42161217
fonte
1
Ou você sabe disso. : P
totallyhuman
1
@totallyhuman Eu não acho isso certo, como Hueé o HSB (AKA HSV) e não o HSL (veja as figuras 2a e 2b na página vinculada da Wikipedia).
Jonathan Allan
1
Jenny - se estiver certo, não há motivo para não publicá-lo, pois a contagem de bytes com o que você não construiu abaixo!
Jonathan Allan
1
@JonathanAllan Oh, assim é. É um pouco chato como RGBColorexiste, mas HSLColornão existe . > _>
totallyhuman
Existe uma versão não destruída disso?
user76284 7/07
4

Python 2 , 32 bytes

from colorsys import*;hls_to_rgb

Experimente online!

Essa funcionalidade é realmente incorporada ao Python por hls_to_rgbdentro da colorsysbiblioteca. O formato das entradas da mina é um intervalo 0-1 de HLS (em vez de HSL, ambos são acrônimos comuns para a mesma coisa [embora HSL seja mais comum]). E a mina gera valores RGB em uma tupla com um intervalo de 0 a 1.

Créditos

Agradecemos a Jonathan Allan por apontar que eu só preciso importá-lo (e depois me ajudar a reduzir a contagem de bytes).

Neil
fonte
Eu acho que isso pode ser apenas 31 bytes, from colorsys import hls_to_rgbuma vez que nos dá uma função que pode ser reutilizada. Editar - faça 21 como from colorsys import*.
Jonathan Allan
1
@JonathanAllan Obrigado, atualizado.
Neil
Na verdade, provavelmente apenas import colorsyspor 15!
Jonathan Allan
@JonathanAllan Nice, atualizado novamente.
Neil
1
por mais verdadeiro que seja, acho que isso afeta um pouco demais o espírito do codegolf: /
towc
4

C ++, 228 221 bytes

-7 bytes graças a Zacharý

#define S(o,n)r[t[int(h[0])/60*3+o]+o-2]=(n+h[2]-c/2)*255;
void C(float*h,int*r){float g=2*h[2]-1,c=(g<0?1+g:1-g)*h[1],a=int(h[0])%120/60.f-1;int t[]={2,2,2,3,1,2,3,3,0,4,2,0,4,1,1,2,3,1};S(0,c)S(1,c*(a<0?1+a:1-a))S(2,0)}

Os argumentos são duas matrizes que têm um tamanho mínimo de 3 elementos (ou seja, float hsl[3]eint rgb[3]

OK. Agora, como isso funciona? O que é esse longo array?

Bem, não é uma matriz longa, é uma intmatriz. Brincadeira à parte, a matriz indica em quantas posições trocamos CX e 0 quando queremos selecionar a permutação correta, + 2. Lembre-se de como R ', G' e B 'são selecionados?

Como R ', G' e B 'são selecionados

Digamos que temos uma ordem "normal" que é {C, X, 0}

Agora, quando a primeira permutação (por exemplo, {C, X, 0}) é selecionada, você não precisa mudar, o que é exatamente a mesma coisa que o deslocamento à direita por 0. Portanto, os três primeiros elementos da matriz são 0,0 e 0 0

Para a segunda permutação ({X, C, 0}), precisamos deslocar à direita C por 1, e deslocar à esquerda X por -1, para que o quarto, quinto e sexto elemento da matriz sejam 1, -1 e 0

E assim por diante...

No caso da 3ª e da 4ª permutação, preciso deslocar para a esquerda o 0 por 2, para a direita para -2. Como existem mais de 2 números negativos, prefiro adicionar 2 a cada elemento do array e subtrair 2 no tempo de execução (por razões de golfe, ter apenas números inteiros no array).

HatsuPointerKun
fonte
221 bytes: pastebin.com/C8WaSSpb
Zacharý
4

Python 2 , 119 112 bytes

def f(h,s,l):c=(1-abs(2*l-1))*s;m=l-c/2;x=c*(1-abs(h%2-1))+m;c+=m;return[c,m,x,c,m,x,m,c,x,m][7-int(h)*5%9:][:3]

Experimente online!

Aceita entrada como H=[0,6[, S=[0,1], L=[0,1]

TFeld
fonte
3

HTML + JavaScript (ES6), 8 + 88 = 96 bytes

f=
(h,s,l)=>(p.style.color=`hsl(${h},${s}%,${l}%)`,getComputedStyle(p).color.match(/\d+/g))
<div oninput=o.textContent=f(h.value,s.value,l.value)>H: <input type=number min=0 max=360 value=0 id=h>°<br>S: <input type=number min=0 max=100 value=0 id=s>%<br>L: <input type=number min=0 max=100 value=0 id=l>%<pre id=o>0,0,0</pre>
<p id=p>

Toma a entrada como um ângulo e duas porcentagens. Estou marcando o snippet HTML <p id=p>e a função de seta JavaScript. Não sei o que os navegadores de arredondamento usam, portanto, minhas respostas podem diferir ligeiramente das suas.

Neil
fonte
2

Swift 4, 218 bytes

Aceita valores HSL no intervalo [0,1] como argumentos e retorna uma matriz que contém os valores RGB no mesmo intervalo:

import Cocoa;typealias F=CGFloat;func f(h:F,s:F,l:F)->[F]{var r=F(),g=F(),b=F(),x=s*min(1-l,l),n=2*x/max(l+x,1e-9);NSColor(hue:h,saturation:n,brightness:l+x,alpha:1).getRed(&r,green:&g,blue:&b,alpha:nil);return[r,g,b]}

A idéia é primeiro converter a entrada de HSL para HSB e, em seguida, usar o construtor HSB do objeto de cor incorporado no Cocoa para recuperar os valores RGB.

A versão do UIKit tem exatamente o mesmo comprimento, pois as únicas substituições são:

  • CocoaUIKit
  • NSColorUIColor

Ungolfed:

#if os(iOS)
    import UIKit
    typealias Color = UIColor
#elseif os(OSX)
    import Cocoa
    typealias Color = NSColor
#endif

typealias F = CGFloat

func f(h: F,s: F,l: F) -> [F] {
    var r = F(), g = F(), b = F()

    let x = s * min(1 - l, l)
    let n = 2 * x / max(l + x, 1e-9)

    let color = Color(hue: h, saturation: n, brightness: l + x, alpha: 1)
    color.getRed(&r, green: &g,blue: &b, alpha: nil)

    return [r, g, b]
}

O seguinte fragmento pode ser usada para testar, por apenas substituindo os valores de h, se l:

let h: CGFloat = 0
let s: CGFloat = 0
let l: CGFloat = 0

let result = f(h: h/360, s: s/100, l: l/100).map { Int(round($0*255)) }
print(result)

Infelizmente, não posso fornecer um link para uma sandbox on-line, pois todos eles rodam no Linux, o que não inclui o cacau.

xoudini
fonte
2

GLSL (GL_ES) 160 144 134 131 bytes

Matiz está no intervalo de 0 a 6 (economiza 3 bytes)
Sat, luz e rgb são de 0 a 1

vec3 f(vec3 c){return mix(c.bbb,mix(clamp(vec3(-1,2,2)-abs(c.r-vec3(3,2,4))*vec3(-1,1,1),0.,1.),vec3(c.b>.5),abs(.5-c.b)*2.),c.g);}

Experimente no livro de shaders

Utilizou a ferramenta colorpicker em uma tela de impressão para testar a saída correta,
exceto um pequeno erro em 202 19 39 → 81 104 118, que forneceu 80 104 118

PrincePolka
fonte
1

R , 88 bytes

function(h,s,l){v=(2*l+s*(1-abs(2*l-1)))/2;col2rgb(hsv(h,ifelse(v==0,v,(2*(v-l))/v),v))}

Experimente online!

Essa função usa como entrada os valores H, S e L como valores de 0-1 e varia os valores RGB como 0-255. Ele converte a representação HSL em uma representação HSV, depois usa hsv()para converter a representação HSV em uma cor R (ou seja, representação hexadecimal - #rrggbb) e depois col2rgb()converte a cor R em valores RGB.

duckmayr
fonte
1

Geléia ,  39  32 bytes

-1 graças a caird coinheringaahing ( %2é apenas )
-1 e / ou inspiração para -4 graças a caird coinheringaahing também!

Ḥ;Ḃ’ACש"⁵×\;0+³_®H¤⁴Ḟị336Œ?¤¤œ?

Um programa completo com três argumentos de linha de comando:

  • L, H, SNas gamas de [0,1), [0,6)e [0,1)respectivamente

que imprime uma lista com o formato RGB como

  • [R, G, B] com cada um dos três valores no intervalo [0,1]

Nota: Htambém pode quebrar como [0,360)faria.

Experimente online!

Quão?

Ḥ;Ḃ’ACש"⁵×\;0+³_®H¤⁴Ḟị336Œ?¤¤œ? - Main link: number, L (in [0,1]); number, H (in [0,6))
Ḥ                                - double                 =     2L
  Ḃ                              - modulo by 2            =              H%2
 ;                               - concatenate            = [   2L   ,   H%2]

   ’                             - decrement              = [   2L-1 ,   H%2-1]
    A                            - absolute               = [  |2L-1|,  |H%2-1|]
     C                           - complement             = [1-|2L-1|,1-|H%2-1|]
                                 -                        = [C/S     ,C/X]
         ⁵                       - 3rd program argument   = S  (in [0,1])
        "                        - zip with:
      ×                          -   multiplication       = [C,C/X]
       ©                         -   (copy the result, C, to the register)
           \                     - cumulative reduce with:
          ×                      -   multiplication       = [C, C/X*C] = [C,X]
            ;0                   - concatenate zero       = [C,X,0]
               ³                 - 1st program argument   = L
              +                  - add (vectorises)       = [C+L,X+L,L]
                   ¤             - nilad followed by link(s) as a nilad:
                 ®               -   recall from register = C
                  H              -   halve                = C/2
                _                - subtract               = [C+L-C/2,X+L-C/2,L-C/2]
                             ¤   - nilad followed by link(s) as a nilad:
                    ⁴            -   2nd program argument = H
                     Ḟ           -   floor                = floor(H)
                            ¤    -   nilad followed by link(s) as a nilad:
                       336       -     literal 336
                          Œ?     -     Shortest permutation of natural numbers with a
                                 -     lexicographical index of 336 in all permutations
                                 -     thereof            = [3,5,6,4,2,1]
                      ị          -   index into (1-indexed & modular)
                              œ? - get that permutation of [C+L-C/2,X+L-C/2,L-C/2]
                                 - implicit print
Jonathan Allan
fonte
Opa, é claro. Obrigado!
Jonathan Allan