使用Python PIL修改图像色调

时间:2021-04-14 00:23:43

Using Python PIL, I'm trying to adjust the hue of a given image.

使用Python PIL,我尝试调整给定图像的色调。

I'm not very comfortable with the jargon of graphics, so what I mean by “adjusting hue” is doing the Photoshop operation called “Hue/saturation”: this is to change the color of the image uniformly as shown below:

我对图形的术语不太熟悉,所以我所说的“调整色调”是做一个叫做“色调/饱和度”的Photoshop操作:这是为了均匀地改变图像的颜色,如下图所示:

  • Original: 使用Python PIL修改图像色调
  • 原:
  • With hue adjusted to +180 (red): 使用Python PIL修改图像色调
  • 色调调整为+180(红色):
  • With hue adjusted to -78 (green): 使用Python PIL修改图像色调
  • 色调调整至-78(绿色):

FYI, Photoshop uses a scale of -180 to +180 for this hue setting (where -180 equals +180), that may represents the HSL hue scale (expressed in 0-360 degree).

顺便提一下,Photoshop在这个色调设置中使用了-180到+180的比例(-180等于+180),这可能代表HSL色调比例(以0-360度表示)。

What I'm looking for is a function that, given an PIL image and a float hue within [0, 1] (or int within [0, 360], it doesn't matter), returns the image with its hue shifted by hue as in the example above.

我要找的是一个函数,给定一个PIL图像和一个[0,1]内的浮动色调(或者在[0,360]内的int,这没有关系),返回一个图像,它的色调由上例中的色调移动。

What I've done so far is ridiculous and obviously doesn't give the desired result. It just half-blend my original image with a color-filled layer.

到目前为止,我所做的一切都是荒谬可笑的,显然并没有达到预期的效果。它只是将我的原始图像与填充了颜色的图层混合了一半。

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-) result: 使用Python PIL修改图像色调

(请't-laugh-at)结果:

Thanks in advance!

提前谢谢!


Solution: here is the unutbu code updated so it fits exactly what I've described.

解决方案:这里更新了unutbu代码,因此它完全符合我所描述的。

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 个解决方案

#1


16  

There is Python code to convert RGB to HSV (and vice versa) in the colorsys module in the standard library. My first attempt used

在标准库中的colorsys模块中,有Python代码可以将RGB转换为HSV(反之亦然)。我第一次尝试使用

rgb_to_hsv=np.vectorize(colorsys.rgb_to_hsv)
hsv_to_rgb=np.vectorize(colorsys.hsv_to_rgb)

to vectorize those functions. Unfortunately, using np.vectorize results in rather slow code.

这些函数进行向量化。不幸的是,使用np。向量化会导致相当慢的代码。

I was able to obtain roughly a 5 times speed up by translating colorsys.rgb_to_hsv and colorsys.hsv_to_rgb into native numpy operations.

通过翻译colorsys,我可以提高大约5倍的速度。rgb_to_hsv colorsys。将hsv_to_rgb转换为本机numpy操作。

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')

yields

收益率

使用Python PIL修改图像色调

and

使用Python PIL修改图像色调

#2


10  

With a recent copy of Pillow, one should probably use

有了最近的一本枕头,你或许应该使用它

def rgb2hsv(image):
    return image.convert('HSV')

#3


1  

Good question. PIL does not convert to to a HSV or HSL colorspace, but this is the conversion you need to do in order to alter the hue without any changes to the lightness and saturation of the image.

好问题。PIL不会转换为HSV或HSL颜色空间,但这是您需要进行的转换,以便在不改变图像的亮度和饱和度的情况下更改色调。

What you need to do is convert to HSV, then increment all the H values by some degree, then convert back to RGB.

你需要做的是转换到HSV,然后将所有H值增加一定程度,然后再转换回RGB。

Half the work is done for you in an answer (by me) some time ago. It employs another python module called NumPy and converts RGB colorspace to HSV. It would not be too much trouble to write the reverse conversion.

不久前我给你做了一半的工作。它使用另一个名为NumPy的python模块,并将RGB colorspace转换为HSV。写逆向转换不会太麻烦。

#1


16  

There is Python code to convert RGB to HSV (and vice versa) in the colorsys module in the standard library. My first attempt used

在标准库中的colorsys模块中,有Python代码可以将RGB转换为HSV(反之亦然)。我第一次尝试使用

rgb_to_hsv=np.vectorize(colorsys.rgb_to_hsv)
hsv_to_rgb=np.vectorize(colorsys.hsv_to_rgb)

to vectorize those functions. Unfortunately, using np.vectorize results in rather slow code.

这些函数进行向量化。不幸的是,使用np。向量化会导致相当慢的代码。

I was able to obtain roughly a 5 times speed up by translating colorsys.rgb_to_hsv and colorsys.hsv_to_rgb into native numpy operations.

通过翻译colorsys,我可以提高大约5倍的速度。rgb_to_hsv colorsys。将hsv_to_rgb转换为本机numpy操作。

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')

yields

收益率

使用Python PIL修改图像色调

and

使用Python PIL修改图像色调

#2


10  

With a recent copy of Pillow, one should probably use

有了最近的一本枕头,你或许应该使用它

def rgb2hsv(image):
    return image.convert('HSV')

#3


1  

Good question. PIL does not convert to to a HSV or HSL colorspace, but this is the conversion you need to do in order to alter the hue without any changes to the lightness and saturation of the image.

好问题。PIL不会转换为HSV或HSL颜色空间,但这是您需要进行的转换,以便在不改变图像的亮度和饱和度的情况下更改色调。

What you need to do is convert to HSV, then increment all the H values by some degree, then convert back to RGB.

你需要做的是转换到HSV,然后将所有H值增加一定程度,然后再转换回RGB。

Half the work is done for you in an answer (by me) some time ago. It employs another python module called NumPy and converts RGB colorspace to HSV. It would not be too much trouble to write the reverse conversion.

不久前我给你做了一半的工作。它使用另一个名为NumPy的python模块,并将RGB colorspace转换为HSV。写逆向转换不会太麻烦。