使用 Numpy 更有效地改变色调
Shift hue more efficiently, using Numpy
我正在制作一个在线图像编辑器,我正在实现一个 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
该函数接受一个形状为 (height, width, 3) 的 numpy 数组。我逐个像素地进行处理,然后将色相、饱和度和亮度值添加到每个像素。它需要 13 秒(在 (648, 1152, 3) 形状的数组上)但是,显然太长了。是否有一个 numpy 函数可以将所有值抵消我给它的数量。
p.s。该功能还没有工作,hue 似乎可以,但是 sat 和 lum 没有给出正确的图像。
由于您还没有让 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
我不认为你的算法或接受的答案是正确的 - 如果你将 pixels
数组创建为 uint8
(而不是 int64
,你会看到这个当前是)如果您将输出限制在 0..255.
范围内,这大概就是您所拥有的
您需要将色相与饱和度和亮度区别对待。色调是圆形的,这意味着它 "wraps around" 一个 0..255 "degrees" 的圆。这意味着当它达到 255 并且你加 1 时,你应该回到零并再次绕过。在数学上,这意味着模数 %
。饱和度和亮度不是圆形的,这意味着如果图像几乎完全明亮,比如 250,如果你将亮度增加 100,它应该最大 "burn out"。在数学上,这是 "clipping"。饱和度也一样。
所以,我相信你想要更像这样的东西:
#!/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。您可以像这样使用 -modulate 运算符:
magick INPUTIMAGE -modulate BRIGHTNESS,SATURATION,HUE OUTPUTIMAGE
例如亮度减半:
magick input.png -modulate 50 result.jpg
要将亮度保持在其先前值的 100% 不变,将饱和度增加 20% 并将色相逆时针旋转 90 度(因为 180 的 50% 是 90):
magic input.png -modulate 100,120,50 result.jpg
关键字:Python,图像处理,色调旋转,HSL,HSV,调制。
我正在制作一个在线图像编辑器,我正在实现一个 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
该函数接受一个形状为 (height, width, 3) 的 numpy 数组。我逐个像素地进行处理,然后将色相、饱和度和亮度值添加到每个像素。它需要 13 秒(在 (648, 1152, 3) 形状的数组上)但是,显然太长了。是否有一个 numpy 函数可以将所有值抵消我给它的数量。 p.s。该功能还没有工作,hue 似乎可以,但是 sat 和 lum 没有给出正确的图像。
由于您还没有让 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
我不认为你的算法或接受的答案是正确的 - 如果你将 pixels
数组创建为 uint8
(而不是 int64
,你会看到这个当前是)如果您将输出限制在 0..255.
您需要将色相与饱和度和亮度区别对待。色调是圆形的,这意味着它 "wraps around" 一个 0..255 "degrees" 的圆。这意味着当它达到 255 并且你加 1 时,你应该回到零并再次绕过。在数学上,这意味着模数 %
。饱和度和亮度不是圆形的,这意味着如果图像几乎完全明亮,比如 250,如果你将亮度增加 100,它应该最大 "burn out"。在数学上,这是 "clipping"。饱和度也一样。
所以,我相信你想要更像这样的东西:
#!/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。您可以像这样使用 -modulate 运算符:
magick INPUTIMAGE -modulate BRIGHTNESS,SATURATION,HUE OUTPUTIMAGE
例如亮度减半:
magick input.png -modulate 50 result.jpg
要将亮度保持在其先前值的 100% 不变,将饱和度增加 20% 并将色相逆时针旋转 90 度(因为 180 的 50% 是 90):
magic input.png -modulate 100,120,50 result.jpg
关键字:Python,图像处理,色调旋转,HSL,HSV,调制。