Cambiar el tono de la imagen con Python PIL
Usando Python PIL, estoy tratando de ajustar el tono de una imagen dada.
No estoy muy cómodo con la jerga de los gráficos, así que lo que quiero decir con "ajuste de tono" es hacer la operación de Photoshop llamada "Tono / saturación" : esto es para cambiar el color de la imagen de manera uniforme como se muestra a continuación:
- Original:
- Con tono ajustado a +180 (rojo):
- Con tono ajustado a -78 (verde):
Para su información, Photoshop utiliza una escala de -180 a +180 para este ajuste de tono (donde -180 es igual a +180), puede representar la escala de tono HSL (expresada en 0-360 grados).
Lo que estoy buscando es una función que, dada una imagen PIL y un flotador matiz dentro de [0, 1] (o int dentro de [0, 360], no importa), devuelve la imagen con su matiz desplazado por matiz como en el ejemplo anterior.
Lo que he hecho hasta ahora es ridículo y obviamente no da el resultado deseado. Simplemente mezcla a medias mi imagen original con una capa llena de color.
import Image
im = Image.open('tweeter.png')
layer = Image.new('RGB', im.size, 'red') # "hue" selection is done by choosing a color...
output = Image.blend(im, layer, 0.5)
output.save('output.png', 'PNG')
(Please-don't-laugh-at-) resultado:
Gracias de antemano!
Solución: aquí está el código unutbu actualizado para que se ajuste exactamente a lo que he descrito.
import Image
import numpy as np
import colorsys
rgb_to_hsv = np.vectorize(colorsys.rgb_to_hsv)
hsv_to_rgb = np.vectorize(colorsys.hsv_to_rgb)
def shift_hue(arr, hout):
r, g, b, a = np.rollaxis(arr, axis=-1)
h, s, v = rgb_to_hsv(r, g, b)
h = hout
r, g, b = hsv_to_rgb(h, s, v)
arr = np.dstack((r, g, b, a))
return arr
def colorize(image, hue):
"""
Colorize PIL image `original` with the given
`hue` (hue within 0-360); returns another PIL image.
"""
img = image.convert('RGBA')
arr = np.array(np.asarray(img).astype('float'))
new_img = Image.fromarray(shift_hue(arr, hue/360.).astype('uint8'), 'RGBA')
return new_img
3 answers
Hay código Python para convertir RGB a HSV (y viceversa) en el módulo colorsys en la biblioteca estándar. Mi primer intento utilizado
rgb_to_hsv=np.vectorize(colorsys.rgb_to_hsv)
hsv_to_rgb=np.vectorize(colorsys.hsv_to_rgb)
Para vectorizar esas funciones. Desafortunadamente, usar np.vectorize
resulta en un código bastante lento.
Pude obtener aproximadamente una velocidad de 5 veces traduciendo colorsys.rgb_to_hsv
y colorsys.hsv_to_rgb
en operaciones numpy nativas.
import Image
import numpy as np
def rgb_to_hsv(rgb):
# Translated from source of colorsys.rgb_to_hsv
# r,g,b should be a numpy arrays with values between 0 and 255
# rgb_to_hsv returns an array of floats between 0.0 and 1.0.
rgb = rgb.astype('float')
hsv = np.zeros_like(rgb)
# in case an RGBA array was passed, just copy the A channel
hsv[..., 3:] = rgb[..., 3:]
r, g, b = rgb[..., 0], rgb[..., 1], rgb[..., 2]
maxc = np.max(rgb[..., :3], axis=-1)
minc = np.min(rgb[..., :3], axis=-1)
hsv[..., 2] = maxc
mask = maxc != minc
hsv[mask, 1] = (maxc - minc)[mask] / maxc[mask]
rc = np.zeros_like(r)
gc = np.zeros_like(g)
bc = np.zeros_like(b)
rc[mask] = (maxc - r)[mask] / (maxc - minc)[mask]
gc[mask] = (maxc - g)[mask] / (maxc - minc)[mask]
bc[mask] = (maxc - b)[mask] / (maxc - minc)[mask]
hsv[..., 0] = np.select(
[r == maxc, g == maxc], [bc - gc, 2.0 + rc - bc], default=4.0 + gc - rc)
hsv[..., 0] = (hsv[..., 0] / 6.0) % 1.0
return hsv
def hsv_to_rgb(hsv):
# Translated from source of colorsys.hsv_to_rgb
# h,s should be a numpy arrays with values between 0.0 and 1.0
# v should be a numpy array with values between 0.0 and 255.0
# hsv_to_rgb returns an array of uints between 0 and 255.
rgb = np.empty_like(hsv)
rgb[..., 3:] = hsv[..., 3:]
h, s, v = hsv[..., 0], hsv[..., 1], hsv[..., 2]
i = (h * 6.0).astype('uint8')
f = (h * 6.0) - i
p = v * (1.0 - s)
q = v * (1.0 - s * f)
t = v * (1.0 - s * (1.0 - f))
i = i % 6
conditions = [s == 0.0, i == 1, i == 2, i == 3, i == 4, i == 5]
rgb[..., 0] = np.select(conditions, [v, q, p, p, t, v], default=v)
rgb[..., 1] = np.select(conditions, [v, v, v, q, p, p], default=t)
rgb[..., 2] = np.select(conditions, [v, p, t, v, v, q], default=p)
return rgb.astype('uint8')
def shift_hue(arr,hout):
hsv=rgb_to_hsv(arr)
hsv[...,0]=hout
rgb=hsv_to_rgb(hsv)
return rgb
img = Image.open('tweeter.png').convert('RGBA')
arr = np.array(img)
if __name__=='__main__':
green_hue = (180-78)/360.0
red_hue = (180-180)/360.0
new_img = Image.fromarray(shift_hue(arr,red_hue), 'RGBA')
new_img.save('tweeter_red.png')
new_img = Image.fromarray(shift_hue(arr,green_hue), 'RGBA')
new_img.save('tweeter_green.png')
Rinde
Y
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-20 18:46:47
Con una copia reciente de Pillow, probablemente se debería usar
def rgb2hsv(image):
return image.convert('HSV')
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-20 09:50:53
Buena pregunta. PIL no convierte a un espacio de color HSV o HSL, pero esta es la conversión que necesita hacer para alterar el tono sin ningún cambio en la luminosidad y saturación de la imagen.
Lo que necesita hacer es convertir a HSV, luego incrementar todos los valores de H en algún grado, luego volver a convertir a RGB.
La mitad del trabajo está hecho para ti en una respuesta (por mí) hace algún tiempo. Emplea otro módulo de python llamado NumPy y convierte el espacio de color RGB a HSV. No sería demasiado problema escribir la conversión inversa.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:43