ruido - Cambiar el tono de la imagen con Python PIL
ruido gaussiano python (3)
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, entonces lo que quiero decir con "ajustar el tono" es hacer la operación de Photoshop llamada “Hue/saturation” : esto es para cambiar el color de la imagen uniformemente como se muestra a continuación:
- Original:
- Con tono ajustado a +180 (rojo):
- Con matiz ajustado a -78 (verde):
FYI, Photoshop usa una escala de -180 a +180 para este ajuste de matiz (donde -180 es igual a +180), que puede representar la escala de matiz HSL (expresada en 0-360 grados).
Lo que estoy buscando es una función que, dada una imagen PIL y un tono de flotación dentro de [0, 1] (o int dentro de [0, 360], no importa), devuelve la imagen con su matiz desplazado por tono 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'')
(Por favor, no te rías de) resultado:
¡Gracias por adelantado!
Solución : aquí está el código unutbu actualizado para que encaje exactamente con lo que describí.
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
Buena pregunta. PIL no se 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 claridad y saturación de la imagen.
Lo que necesita hacer es convertir a HSV, luego incrementar todos los valores de H en cierto grado, luego convertir de nuevo a RGB.
La mitad del trabajo está hecho para ti en una answer (por mí) hace algún tiempo. Emplea otro módulo de python llamado NumPy y convierte el espacio de color RGB en HSV. No sería demasiado problema escribir la conversión inversa.
Con una copia reciente de Pillow, probablemente uno debería usar
def rgb2hsv(image):
return image.convert(''HSV'')
Existe un código Python para convertir RGB a HSV (y viceversa) en el módulo colorsys en la biblioteca estándar . Mi primer intento usado
rgb_to_hsv=np.vectorize(colorsys.rgb_to_hsv)
hsv_to_rgb=np.vectorize(colorsys.hsv_to_rgb)
para vectorizar esas funciones. Lamentablemente, el uso de np.vectorize
da np.vectorize
resultado un código bastante lento.
Pude obtener aproximadamente 5 veces más velocidad 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'')
rendimientos
y