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: Original
  • Con tono ajustado a +180 (rojo): tono: -180
  • Con tono ajustado a -78 (verde): tono: -78

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: salida.png

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
Author: zopieux, 2011-09-01

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

introduzca la descripción de la imagen aquí

Y

introduzca la descripción de la imagen aquí

 17
Author: unutbu,
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')
 10
Author: K3---rnc,
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.

 1
Author: Paul,
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