2012-10-03 58 views
5

Estoy trabajando en una aplicación que crea imágenes cuyo tono, saturación y valor cambian de acuerdo con diferentes parámetros. Por motivos de rendimiento, tendría sentido representar los componentes de tono, saturación y valor por separado, y luego combinarlos mediante modos de fusión al estilo de Photoshop (multiplicar, superponer, pantalla, tono, etc.).Recrear color HSV utilizando modos de fusión

Ya sé cómo hacer esto para las imágenes RGB: divida cada canal en su propia imagen roja, verde o azul con valores que van desde transparente hasta el color de ese canal. La capa de ellos juntos en la parte superior de negro y establecer su modo de fusión a la pantalla, y ¡listo, usted tiene su imagen en color:

RGB image made from components

¿Cómo voy a hacer esto con una imagen definida por los valores de VHS? Mi aplicación a menudo cambia uno de estos canales sin cambiar los otros dos, y aceleraría mi renderizado si pudiera componer imágenes existentes en la GPU en lugar de renderizar una imagen completamente nueva cada vez que algo cambia.

Aquí está un ejemplo: an image generated with separate H, S, and V channels

En este ejemplo, el tono varía de 0º a 360º alrededor de la circunferencia, la saturación varía de 0% a 100% desde el centro hasta el borde, y el brillo (V) varía de 0% a 100% alrededor de la circunferencia. Esto es típico del tipo de imagen que crea mi aplicación. ¿Existe una combinación de modos de fusión comunes que podría utilizar para crear estos canales por separado y compilarlos de una manera matemáticamente perfecta?

+0

Creo que es una pregunta muy interesante. Estoy tratando de descubrir la respuesta también. ¿Encontraste algo? – Alejandro

+0

En realidad estaba jugando con esto recientemente. No encontré una solución exacta, pero Screen se acerca. Una imagen que creé en Photoshop en realidad terminó viéndose mejor (colores más brillantes y más saturados) que la versión generada por código, pero por supuesto si buscas precisión, eso no servirá para nada. ¿Tal vez podrías usar alguna combinación de modos de mezcla de Matiz y Luminosidad con capas duplicadas? –

Respuesta

6

Mi aplicación a menudo cambia uno de estos canales sin cambiar los otros dos, y aceleraría mi renderizado si pudiera componer imágenes existentes en la GPU en lugar de representar una imagen completamente nueva cada vez que algo cambia. [OP, @ZevEisenberg]

Con respecto a la solidez y la GPU, que acababa de tirar a conversion function en un fragment shader (e.g.). Esto leería HSV almacenado en una textura o tres texturas diferentes, haga la conversión por píxel y la salida RGB. Bonito y fácil. No veo ningún beneficio al no cambiar otras capas ya que H, S o V afectarán a todos los canales RGB. Tal vez almacenar resultados RGB intermedios como hue=hsv2rgb(H,1,1) y actualizar con final=(hue*S+1-S)*V, almacenar en caché hue-to-rgb, pero no creo que valga la pena.

De todos modos, cada modo de mezcla tiene una fórmula simple, y se podía unirlos para el VHS que involucra un conjunto extraordinariamente complejo de texturas intermedias, pero será mucho más lento, principalmente debido al ancho de banda de almacenamiento y la memoria temporal innecesaria. Por no hablar, tratando de volver a escribir la fórmula en funciones de mezcla suena bastante difícil, lo que con ramificación, divisiones, fract, sujeción, etc ... absolutos

Estoy muy interesado en una solución para la división de una imagen en sus componentes HSV y recrean la imagen original usando modos de fusión en Photoshop. [Bounty, @phisch]

Con respecto al photoshop ... No estoy hecho de dinero. Entonces en gimp, hay Colours -> Components -> Compose/Decompose que hace esto por usted. Me sorprendería que esto no exista en Photoshop, pero tampoco es así. Tal vez hay scripts/plugins de photoshop que podrían hacerlo si no? Pero dijiste específicamente blending. Su pregunta podría obtener una mejor atención al https://graphicdesign.stackexchange.com/.A continuación, he dado una idea de la complejidad involucrada y dudo que Photoshop pueda hacerlo. Puede haber formas en torno a los valores de píxel por fuera de 0 a 1, pero luego podría encontrarse con problemas de precisión, simplemente no debería hacerse.


De todos modos, un desafío es un desafío a pesar de lo poco práctico. Lo siguiente es solo por diversión.

Voy a empezar con la siguiente función (de here) y tres texturas ... HSV

vec3 hsv2rgb(vec3 c) 
{ 
    vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0); 
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 
} 

h h h

Sólo sé OpenGL y no estoy seguro de cómo haría esto sin texturas de coma flotante o algunas de las funciones de fusión extendidas, así que las estoy usando. Pero solo puedo mezclar (sin sombreadores). Para las constantes haré texturas con (1,1,1), (1,2/3,1/3), (3,3,3), (6,6,6) (1/255,1/255,1/255), (255,255,255), (1/2,1/2,1/2) y (0,0,0) porque no pude obtener GL_ZERO para escalar con GL_DIFFERENCE_NV.

  1. comienzo con textura tonalidad
  2. mezcla uso de aditivos para agregar (1,2/3,1/3)
  3. encontrar la parte fraccional

    1. con mezcla sustractiva, restar 0,5 (esto es para un floor() ya que estoy asumiendo GL colores colores al convertir a 8 bits. Si no, omita esto)
    2. escalado por 1/255. esto podría hacerse con mezclas alfa regulares, pero en cambio, he escalado con una textura de color.
    3. pase a través de un punto de la textura no flotante para redondear a la escala 1/255
    4. más cercana copia de seguridad de 255 (de nuevo en un punto de la textura flotante)

      integer

    5. Ahora tenemos la componente entero. restar esto desde lo que empezamos con

      fractional

  4. escala 6

  5. con la mezcla sustractiva, tomar 3
  6. tomar el valor absoluto de la

    estoy simplemente use GL_DIFFERENCE_NV para esto, pero sin él podría haber una forma de usar dos abrazaderas separadas para el próximo paso. ya que los negativos se pinzarán de todos modos, algo similar a clamp(p-K.xxx,0,1) + clamp(-p-K.xxx,0,1).

  7. restar 1

    enter image description here bueno, eso es tonalidad hecho

  8. podría sujetar al pasar por un punto de la textura no flotante, pero sólo va a utilizar GL_MIN

  9. ahora podía utilizar alfa mezcla para mix(), pero la saturación se carga como una imagen en blanco y negro sin un canal alfa. ya que es la mezcla de blanco, hacerlo a mano es realmente más fácil ...

    escala por la saturación

  10. añadir al menos 1
  11. restar la saturación

    enter image description here y la saturación se ha aplicado

  12. escala por valor

    enter image description here y está la imagen

  13. café

Todo hecho utilizando

  • glBlendEquation con GL_FUNC_REVERSE_SUBTRACT, GL_MIN y GL_DIFFERENCE_NV
  • glBlendFunc

Aquí está mi código ...

//const tex init 
constTex[0] = makeTex() with 1, 1, 1... 
constTex[1] = makeTex() with 1, 2/3, 1/3... 
constTex[2] = makeTex() with 3, 3, 3... 
constTex[3] = makeTex() with 6, 6, 6... 
constTex[4] = makeTex() with 1/255, 1/255, 1/255... 
constTex[5] = makeTex() with 255, 255, 255... 
constTex[6] = makeTex() with 1/2, 1/2, 1/2... 
constTex[7] = makeTex() with 0, 0, 0... 

... 

fbo[0] = makeFBO() with GL_RGB 
fbo[1] = makeFBO() with GL_RGB32F 
fbo[2] = makeFBO() with GL_RGB32F 

... 


hsv[0] = loadTex() hue 
hsv[1] = loadTex() value 
hsv[2] = loadTex() saturation 

... 

fbo[1].bind(); 
glDisable(GL_BLEND); 
draw(hsv[0]); //start with hue 
glEnable(GL_BLEND); 
glBlendEquation(GL_FUNC_ADD); 
glBlendFunc(GL_ONE, GL_ONE); //add 
draw(constTex[1]); //(1, 2/3, 1/3) 
glBlendFunc(GL_ONE, GL_ONE); 
fbo[1].unbind(); 

//compute integer part 
fbo[2].bind(); 
glDisable(GL_BLEND); 
draw(*fbo[1].colour[0]); //copy the last bit 
glEnable(GL_BLEND); 
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); 
glBlendFunc(GL_ONE, GL_ONE); //subtract 
draw(constTex[6]); //0.5 
glBlendEquation(GL_FUNC_ADD); 
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale down 
draw(constTex[4]); //1/255 
fbo[2].unbind(); 

fbo[0].bind(); //floor to integer 
glDisable(GL_BLEND); 
draw(*fbo[2].colour[0]); 
fbo[0].unbind(); 

fbo[2].bind(); //scale back up 
glDisable(GL_BLEND); 
draw(*fbo[0].colour[0]); 
glEnable(GL_BLEND); 
glBlendEquation(GL_FUNC_ADD); 
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale up 
draw(constTex[5]); //255 
fbo[2].unbind(); 

//take integer part for fractional 
fbo[1].bind(); 
glEnable(GL_BLEND); 
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); 
glBlendFunc(GL_ONE, GL_ONE); //subtract 
draw(*fbo[2].colour[0]); //integer part 
glBlendEquation(GL_FUNC_ADD); 
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale 
draw(constTex[3]); //6 
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); 
glBlendFunc(GL_ONE, GL_ONE); //subtract 
draw(constTex[2]); //3 
glBlendEquation(GL_DIFFERENCE_NV); 
glBlendFunc(GL_ZERO, GL_ONE); //take the absolute 
draw(constTex[7]); //0 
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); 
glBlendFunc(GL_ONE, GL_ONE); //subtract 
draw(constTex[0]); //1 
glBlendEquation(GL_MIN); 
glBlendFunc(GL_ONE, GL_ONE); //clamp (<0 doesn't matter, >1 use min) 
draw(constTex[0]); //1 
glBlendEquation(GL_FUNC_ADD); 
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale 
draw(hsv[1]); //saturation 
glBlendEquation(GL_FUNC_ADD); 
glBlendFunc(GL_ONE, GL_ONE); //add 
draw(constTex[0]); //1 
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); 
glBlendFunc(GL_ONE, GL_ONE); //subtract 
draw(hsv[1]); //saturation 
glBlendEquation(GL_FUNC_ADD); 
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale 
draw(hsv[2]); //saturation 
fbo[1].unbind(); 

fbo[1].blit(); //check result 
Cuestiones relacionadas