Сдвиньте оттенок более эффективно, используя Numpy - PullRequest
2 голосов
/ 30 апреля 2020

Я создаю онлайн-редактор изображений и я использую редактор Hue / Sat / Lum. Вот моя функция для изменения изображения с заданными значениями.

def make_edit(pixels, hue, sat, lum):
    shape = pixels.shape
    new = np.empty(shape)
    print(time.time())
    for row_count, row in enumerate(pixels):
        for pixel_count, p in enumerate(row):
            new_hue = p[0] + hue
            if new_hue < 0:
                new_hue += 255
            elif new_hue > 255:
                new_hue -= 255

            new_sat = p[1] + sat
            if new_sat < 0:
                new_sat += 255
            elif new_sat > 255:
                new_sat -= 255

            new_lum = p[2] + lum
            if new_lum < 0:
                new_lum = 0
            elif new_lum > 255:
                new_lum = 255

            new[row_count, pixel_count] = np.array([new_hue, new_sat, new_lum])
    print(time.time())
    return new

Функция принимает массив numpy в форме (высота, ширина, 3). Я делаю это попиксельно, затем добавляю значение hue, sat и lum к каждому пикселю. Это занимает 13 секунд (на массиве (648, 1152, 3)), однако, очевидно, слишком долго. Есть ли функция numpy, которая может компенсировать все значения на сумму, которую я ей даю. ps функция еще не работает, кажется, оттенок, но sat и lum не дают правильных изображений.

Ответы [ 2 ]

1 голос
/ 01 мая 2020

Я не думаю, что ваш алгоритм или принятый ответ верны - вы увидите это, если создадите массив pixels как uint8 (а не int64, как он есть в настоящее время), который, вероятно, соответствует вашему Имеется, если вы ограничиваете выходы диапазоном 0..255.

Вам необходимо трактовать оттенок иначе, чем насыщенность и легкость. Оттенок является круглым, что означает, что он «оборачивается» кругом 0..255 «градусов» . Это означает, что когда он достигает 255 и вы добавляете 1, вы должны вернуться к нулю и снова набрать go. Математически это означает модуль %. Насыщенность и яркость не круглые, это означает, что если изображение почти полностью яркое, скажем, 250, если вы добавите 100 к яркости, оно должно "выгореть" на максимуме. Математически это «отсечение» . Аналогично с Насыщением.

Итак, я полагаю, вы хотите что-то более похожее на это:

#!/usr/bin/env python3

import numpy as np

def make_edit(im, hue, sat, lum):
    # Make signed and larger to accommodate wrap-around
    im = im.astype(np.int32)

    # Add constant amount of hue to each pixel, wrapping around at 255
    im[:,:,0] = (im[:,:,0] + hue) % 256 

    # Add constant amount of saturation, and lightness to each pixel
    im[:,:,1] += sat
    im[:,:,2] += lum

    # Clip results to range 0..255 and return as uint8
    return np.clip(im,0,255).astype(np.uint8)

# Make our randomness deterministic!
np.random.seed(42)

# Create 4x2 array of HSL pixels - note UINT8
im = np.random.randint(0,255,(2,4,3),dtype=np.uint8) 

# array([[[102, 220, 225],
#        [ 95, 179,  61],
#        [234, 203,  92],
#        [  3,  98, 243]],
#
#       [[ 14, 149, 245],
#        [ 46, 106, 244],
#        [ 99, 187,  71],
#        [212, 153, 199]]], dtype=uint8)

res = make_edit(im, 100, 50, 20)
print(res)

#[[[202 255 245]
#  [195 229  81]
#  [ 78 253 112]
#  [103 148 255]]
#
# [[114 199 255]
#  [146 156 255]
#  [199 237  91]
#  [ 56 203 219]]]

res = make_edit(im, -100, -50, -20)
print(res)

#[[[  2 170 205]
#  [251 129  41]
#  [134 153  72]
#  [159  48 223]]
#
# [[170  99 225]
#  [202  56 224]
#  [255 137  51]
#  [112 103 179]]]

Обратите внимание, что вы можете использовать ImageMagick , если вы хотите проверить ваш код, просто запустив команды в терминале. Вы можете использовать оператор -модулировать следующим образом:

magick INPUTIMAGE -modulate BRIGHTNESS,SATURATION,HUE OUTPUTIMAGE

Например, чтобы уменьшить яркость вдвое:

magick input.png -modulate 50 result.jpg

Чтобы оставить яркость без изменений на 100% его предыдущего значения, увеличьте насыщенность на 20% и поверните оттенок на 90 градусов против часовой стрелки (потому что 50% из 180 - это 90):

magic input.png -modulate 100,120,50 result.jpg

Ключевые слова : Python, обработка изображений, поворот оттенков, HSL, HSV, модуляция.

1 голос
/ 30 апреля 2020

Поскольку у вас еще нет правильной работы sat и lum, все же, вероятно, его необходимо будет скорректировать в соответствии с тем, как будет выглядеть ваш конечный код, однако он соответствует результатам вашего текущего процесса и на несколько порядков быстрее:

def getPic():   
    return  np.random.randint(0, 255, 648*1152*3).reshape(648, 1152, 3)

def make_edit(pixels, hue, sat, lum):
    shape = pixels.shape
    new = np.empty(shape)
    #print(time.time())
    for row_count, row in enumerate(pixels):
        for pixel_count, p in enumerate(row):
            new_hue = p[0] + hue
            if new_hue < 0:
                new_hue += 255
            elif new_hue > 255:
                new_hue -= 255

            new_sat = p[1] + sat
            if new_sat < 0:
                new_sat += 255
            elif new_sat > 255:
                new_sat -= 255

            new_lum = p[2] + lum
            if new_lum < 0:
                new_lum = 0
            elif new_lum > 255:
                new_lum = 255

            new[row_count, pixel_count] = np.array([new_hue, new_sat, new_lum])
    #print(time.time())
    return new


def new_make_edit(pixels, hue, sat, lum):
    new = np.empty_like(pixels)
    new[:,:,0] = pixels[:,:,0] + hue
    new[:,:,0][new[:,:,0]<0] += 255
    new[:,:,0][new[:,:,0]>255] -= 255

    new[:,:,1] = pixels[:,:,1] + sat
    new[:,:,1][new[:,:,1]<0] += 255
    new[:,:,1][new[:,:,1]>255] -= 255

    new[:,:,2] = pixels[:,:,2] + lum
    new[:,:,2][new[:,:,2]<0] = 0
    new[:,:,2][new[:,:,2]>255] = 255
    return new

def tEd():
    pic = getPic()
    old = make_edit(pic, 10, -25, 211)
    new = new_make_edit(pic, 10, -25, 211)
    return old, new

def timeOld():
    pic = getPic()
    old = make_edit(pic, 10, -25, 211)
    return old

def timeNew():
    pic = getPic()
    new = new_make_edit(pic, 10, -25, 211)
    return new

Выполнить старое и новое для одного и того же образа и проверить соответствие выходных данных:

>>> o,n=tEd()
>>> np.all(o==n)
True

Сравнение производительности:

>>> timeit.timeit(timeNew, number=10)
0.5608299169980455
>>> timeit.timeit(timeOld, number=10)
58.86368254100671
...