Подтвердить что ты не робот

Изменение оттенка изображения с помощью Python PIL

Используя Python PIL, я пытаюсь настроить оттенок данного изображения.

Мне не очень нравится использование жаргона графики, так что я имею в виду под "настройкой оттенка" - это операция Photoshop, называемая "Hue/saturation" : это равномерно изменить цвет изображения, как показано ниже:

  • Оригинал: Original
  • С оттенком, настроенным на +180 (красный): hue: -180
  • С оттенком, настроенным на -78 (зеленый): hue: -78

FYI, Photoshop использует шкалу от -180 до +180 для этой настройки оттенка (где -180 равно +180), что может представлять собой шкалу оттенков HSL (выражается в 0-360 градусов).

То, что я ищу, - это функция, которая, учитывая изображение PIL и оттенок float внутри [0, 1] (или int внутри [0, 360], не имеет значения), возвращает изображение с его оттенком, измененным оттенком, как в приведенном выше примере.

То, что я делал до сих пор, смехотворно и явно не дает желаемого результата. Он просто наполовину смешал мое исходное изображение с наполненным цветом слоем.

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

(Пожалуйста, не догадайтесь) результат: output.png

Спасибо заранее!


Решение: здесь обновлен код 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
4b9b3361

Ответ 1

Существует код Python для преобразования RGB в HSV (и наоборот) в colorys module в стандартной библиотеке. Моя первая попытка использовала

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

для векторизации этих функций. К сожалению, использование np.vectorize приводит к довольно медленному коду.

Мне удалось получить примерно в 5 раз быстрее, переведя colorsys.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')

дает

enter image description here

и

enter image description here

Ответ 2

С недавней копией Pillow, вероятно, следует использовать

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

Ответ 3

Хороший вопрос. PIL не конвертируется в цветовое пространство HSV или HSL, но это преобразование, которое вам нужно сделать, чтобы изменить оттенок без каких-либо изменений в легкости и насыщенности изображения.

Что вам нужно сделать, это преобразовать в HSV, а затем в некоторой степени увеличить все значения H, а затем преобразовать обратно в RGB.

Половина работы выполняется для вас в ответе (мной) некоторое время назад. Он использует другой модуль python под названием NumPy и преобразует цветовое пространство RGB в HSV. Было бы не слишком сложно написать обратное преобразование.