仅在 python 中调亮图像的暗区
Brighten only dark areas of image in python
我正在尝试处理图像,我想使图像的暗区变亮。我尝试了直方图均衡化,但是由于图像中也有一些明亮的区域,结果并不令人满意。这就是为什么我正在寻找一种方法来增亮仅图像的黑暗区域。
举个例子,左边是输入图,右边是预期结果,女孩的头发和脸都变亮了
ImageMagick 似乎提供了一些实现此目的的可能性,但是,我想使用 python
来实现
如果您想避免颜色失真,您可以:
- 转换为 HSV 色彩空间,
- 拆分频道,
- 提高 V(值)通道
- 重新组合渠道
- 保存
可能是这样的:
from PIL import Image
# Open the image
im = Image.open('hEHxh.jpg')
# Convert to HSV colourspace and split channels for ease of separate processing
H, S, V = im.convert('HSV').split()
# Increase the brightness, or Value channel
# Change 30 to 50 for bigger effect, or 10 for smaller effect
newV = V.point(lambda i: i + int(30*(255-i)/255))
# Recombine channels and convert back to RGB
res = Image.merge(mode="HSV", bands=(H,S,newV)).convert('RGB')
res.save('result.jpg')
本质上,我将亮度从黑色映射更改为绿色映射:
提醒健忘的自己...“马克,你把情节写成了这样”:
import matplotlib.pyplot as plt
import numpy as np
# Generate some straight-line data
xdata = np.arange(0,256)
# And the new mapping
ydata = xdata + 30*(255-xdata)/255
# Plot
plt.plot(xdata,xdata,'.k')
plt.plot(xdata,ydata,'g^')
plt.title('Adjustment of V')
plt.xlabel('Input V')
plt.ylabel('Output V')
plt.grid(True)
plt.show()
以下是仅使用 cv2
模块即可完成此操作的方法:
import cv2
def lighten(img, value=30):
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
v = hsv[..., 2]
v[:] = cv2.add(v, value)
return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
img = cv2.imread("image.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
mask = cv2.threshold(gray, 175, 255, cv2.THRESH_BINARY)[1] == 0
img[mask] = lighten(img)[mask]
cv2.imshow("result", img)
cv2.waitKey(0)
输入图像和输出图像:
这是在 Imagemagick 和 Python/OpenCV 中执行此操作的一种方法。使用三角法对 LAB 颜色空间的 L 通道进行阈值处理。然后使整个图像变亮。然后使用阈值作为蒙版合并原始图像和增亮图像。
Imagemagick:
magick girl_on_chair.jpg \
\( -clone 0 -colorspace LAB -channel 0 -separate +channel \
-auto-threshold triangle -negate +write thresh.png \) \
\( -clone 0 -evaluate multiply 4 \) \
+swap -compose over -composite \
girl_on_chair_processed.jpg
阈值:
结果:
Python/OpenCV:
import cv2
import numpy as np
# read image
img = cv2.imread("girl_on_chair.jpg")
# convert to LAB and extract L channel
LAB = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
L = LAB[:,:,0]
# threshold L channel with triangle method
value, thresh = cv2.threshold(L, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_TRIANGLE)
print(value)
# threshold with adjusted value
value = value + 10
thresh = cv2.threshold(L, value, 255, cv2.THRESH_BINARY)[1]
# invert threshold and make 3 channels
thresh = 255 - thresh
thresh = cv2.merge([thresh, thresh, thresh])
gain = 3
blue = cv2.multiply(img[:,:,0], gain)
green = cv2.multiply(img[:,:,1], gain)
red = cv2.multiply(img[:,:,2], gain)
img_bright = cv2.merge([blue, green, red])
# blend original and brightened using thresh as mask
result = np.where(thresh==255, img_bright, img)
# save result
cv2.imwrite('girl_on_chair_thresh.jpg', thresh)
cv2.imwrite('girl_on_chair_brighten.jpg', result)
cv2.imshow('img', img)
cv2.imshow('L', L)
cv2.imshow('thresh', thresh)
cv2.imshow('img_bright', img_bright)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
阈值:
结果:
在 Imagemagick 中执行此操作的另一种方法是使用 -fx 来设置阈值并使用 LAB 的 L 通道或 YCbCr 的 Y 通道或 HSV 的 V 通道通过在该通道的位置添加一个常数值来增加亮度小于某个阈值。
(注意:在-fx中,u
等图像的范围从0到1不等。)
magick girl_on_chair.jpg \
-colorspace LAB -separate \
\( -clone 0 -fx "u<0.15?u+0.15:u" \) \
-swap 0,3 +delete \
-set colorspace LAB -combine -colorspace sRGB \
girl_on_chair_proc2.jpg
magick girl_on_chair.jpg \
-colorspace YCBCR -separate \
\( -clone 0 -fx "u<0.15?u+0.15:u" \) \
-swap 0,3 +delete \
-set colorspace YCBCR -combine -colorspace sRGB \
girl_on_chair_proc3.jpg
magick girl_on_chair.jpg \
-colorspace HSV -separate \
\( -clone 2 -fx "u<0.15?u+0.15:u" \) \
+swap +delete \
-set colorspace HSV -combine -colorspace sRGB \
girl_on_chair_proc4.jpg
实验结果:
YCbCr 结果:
HSV 结果:
可以使用Python/OpenCV进行类似的处理,如下:
import cv2
import numpy as np
# read image
img = cv2.imread("girl_on_chair.jpg")
# convert to LAB and extract L channel
LAB = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
L,A,B = cv2.split(LAB)
# process L channel
L_proc = np.where(L<40, cv2.add(L,40), L)
# recombine L_proc with A and B
LAB_new = cv2.merge([L_proc, A, B])
# convert back to BGR
result = cv2.cvtColor(LAB_new, cv2.COLOR_LAB2BGR)
# save result
cv2.imwrite('girl_on_chair_brighten2.jpg', result)
cv2.imshow('img', img)
cv2.imshow('L', L)
cv2.imshow('L_proc', L_proc)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
这是使用 tonelut
operation in pyvips 的解决方案。它应该是高质量的,而且很快。
它使 CIELAB 中的 L* 变亮。对于此类操作,这是一个不错的选择,因为 L* 将亮度分为 100 个刚好可见的差异级。 L* 范围的 20%、50% 和 80% 是阴影、中间调和高光位置的有用经验法则。
它是这样工作的:
- 图像已转换为 LABS 色彩空间。这是 CIELAB,但表示为带符号的短,因此 L* 为 0-32767,a* 和 b*
为 +/- 32767
- 它找到 L*(零)波段的直方图,并使用它来确定此图像的 1% 和 99% 点
- 然后将这些传递给
tonelut
,为生成的色调调整函数设置黑白。用户可以通过 command-line 参数 调整阴影、中间调和高光增强
tonelut
使用一组样条生成平滑单调的色调重映射函数
- L*通过色调调整曲线映射
- 最后,它转换回原始色彩空间并写入
代码:
#!/usr/bin/python3
import sys
import pyvips
input_filename = sys.argv[1]
output_filename = sys.argv[2]
shadow_boost = float(sys.argv[3])
midtone_boost = float(sys.argv[4])
highlight_boost = float(sys.argv[5])
image = pyvips.Image.new_from_file(sys.argv[1])
# we work in LABS format, a 16-bit version of LAB, with 0 - 32767 for L*, and
# +/- 32767 for a* and b* +/- 128
original_interpretation = image.interpretation
image = image.colourspace("labs")
# find the histogram of L* and use it to pick the 1% and 99% points as black
# and white
hist = image[0].hist_find();
norm = hist.hist_cum().hist_norm() # normalised cumulative histogram
black = (norm > norm.width * 0.01).profile()[1].avg()
white = (norm > norm.width * 0.99).profile()[1].avg()
# tonelut wants percentages of 0-32767
black = 100.0 * black / 32767.0
white = 100.0 * white / 32767.0
# make a tone-mapping function using those black and white points
tone = pyvips.Image \
.tonelut(Lb=black, Lw=white, \
S=shadow_boost, M=midtone_boost, H=highlight_boost)
# remap L* of the image
image = (image[0].maplut(tone)).bandjoin(image[1:])
# back to the original interpretation, and save
image = image.colourspace(original_interpretation)
image.write_to_file(output_filename)
我可以运行这样:
$ ./tone.py ~/pics/girl.jpg x.jpg +10 +10 0
这也增强了中间调,因为那是皮肤掉落的地方。这是之前和之后:
我们可以从 better-quality 测试图像中提取更多信息。
我正在尝试处理图像,我想使图像的暗区变亮。我尝试了直方图均衡化,但是由于图像中也有一些明亮的区域,结果并不令人满意。这就是为什么我正在寻找一种方法来增亮仅图像的黑暗区域。
举个例子,左边是输入图,右边是预期结果,女孩的头发和脸都变亮了
ImageMagick 似乎提供了一些实现此目的的可能性,但是,我想使用 python
来实现如果您想避免颜色失真,您可以:
- 转换为 HSV 色彩空间,
- 拆分频道,
- 提高 V(值)通道
- 重新组合渠道
- 保存
可能是这样的:
from PIL import Image
# Open the image
im = Image.open('hEHxh.jpg')
# Convert to HSV colourspace and split channels for ease of separate processing
H, S, V = im.convert('HSV').split()
# Increase the brightness, or Value channel
# Change 30 to 50 for bigger effect, or 10 for smaller effect
newV = V.point(lambda i: i + int(30*(255-i)/255))
# Recombine channels and convert back to RGB
res = Image.merge(mode="HSV", bands=(H,S,newV)).convert('RGB')
res.save('result.jpg')
本质上,我将亮度从黑色映射更改为绿色映射:
提醒健忘的自己...“马克,你把情节写成了这样”:
import matplotlib.pyplot as plt
import numpy as np
# Generate some straight-line data
xdata = np.arange(0,256)
# And the new mapping
ydata = xdata + 30*(255-xdata)/255
# Plot
plt.plot(xdata,xdata,'.k')
plt.plot(xdata,ydata,'g^')
plt.title('Adjustment of V')
plt.xlabel('Input V')
plt.ylabel('Output V')
plt.grid(True)
plt.show()
以下是仅使用 cv2
模块即可完成此操作的方法:
import cv2
def lighten(img, value=30):
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
v = hsv[..., 2]
v[:] = cv2.add(v, value)
return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
img = cv2.imread("image.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
mask = cv2.threshold(gray, 175, 255, cv2.THRESH_BINARY)[1] == 0
img[mask] = lighten(img)[mask]
cv2.imshow("result", img)
cv2.waitKey(0)
输入图像和输出图像:
这是在 Imagemagick 和 Python/OpenCV 中执行此操作的一种方法。使用三角法对 LAB 颜色空间的 L 通道进行阈值处理。然后使整个图像变亮。然后使用阈值作为蒙版合并原始图像和增亮图像。
Imagemagick:
magick girl_on_chair.jpg \
\( -clone 0 -colorspace LAB -channel 0 -separate +channel \
-auto-threshold triangle -negate +write thresh.png \) \
\( -clone 0 -evaluate multiply 4 \) \
+swap -compose over -composite \
girl_on_chair_processed.jpg
阈值:
结果:
Python/OpenCV:
import cv2
import numpy as np
# read image
img = cv2.imread("girl_on_chair.jpg")
# convert to LAB and extract L channel
LAB = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
L = LAB[:,:,0]
# threshold L channel with triangle method
value, thresh = cv2.threshold(L, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_TRIANGLE)
print(value)
# threshold with adjusted value
value = value + 10
thresh = cv2.threshold(L, value, 255, cv2.THRESH_BINARY)[1]
# invert threshold and make 3 channels
thresh = 255 - thresh
thresh = cv2.merge([thresh, thresh, thresh])
gain = 3
blue = cv2.multiply(img[:,:,0], gain)
green = cv2.multiply(img[:,:,1], gain)
red = cv2.multiply(img[:,:,2], gain)
img_bright = cv2.merge([blue, green, red])
# blend original and brightened using thresh as mask
result = np.where(thresh==255, img_bright, img)
# save result
cv2.imwrite('girl_on_chair_thresh.jpg', thresh)
cv2.imwrite('girl_on_chair_brighten.jpg', result)
cv2.imshow('img', img)
cv2.imshow('L', L)
cv2.imshow('thresh', thresh)
cv2.imshow('img_bright', img_bright)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
阈值:
结果:
在 Imagemagick 中执行此操作的另一种方法是使用 -fx 来设置阈值并使用 LAB 的 L 通道或 YCbCr 的 Y 通道或 HSV 的 V 通道通过在该通道的位置添加一个常数值来增加亮度小于某个阈值。
(注意:在-fx中,u
等图像的范围从0到1不等。)
magick girl_on_chair.jpg \
-colorspace LAB -separate \
\( -clone 0 -fx "u<0.15?u+0.15:u" \) \
-swap 0,3 +delete \
-set colorspace LAB -combine -colorspace sRGB \
girl_on_chair_proc2.jpg
magick girl_on_chair.jpg \
-colorspace YCBCR -separate \
\( -clone 0 -fx "u<0.15?u+0.15:u" \) \
-swap 0,3 +delete \
-set colorspace YCBCR -combine -colorspace sRGB \
girl_on_chair_proc3.jpg
magick girl_on_chair.jpg \
-colorspace HSV -separate \
\( -clone 2 -fx "u<0.15?u+0.15:u" \) \
+swap +delete \
-set colorspace HSV -combine -colorspace sRGB \
girl_on_chair_proc4.jpg
实验结果:
YCbCr 结果:
HSV 结果:
可以使用Python/OpenCV进行类似的处理,如下:
import cv2
import numpy as np
# read image
img = cv2.imread("girl_on_chair.jpg")
# convert to LAB and extract L channel
LAB = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
L,A,B = cv2.split(LAB)
# process L channel
L_proc = np.where(L<40, cv2.add(L,40), L)
# recombine L_proc with A and B
LAB_new = cv2.merge([L_proc, A, B])
# convert back to BGR
result = cv2.cvtColor(LAB_new, cv2.COLOR_LAB2BGR)
# save result
cv2.imwrite('girl_on_chair_brighten2.jpg', result)
cv2.imshow('img', img)
cv2.imshow('L', L)
cv2.imshow('L_proc', L_proc)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
这是使用 tonelut
operation in pyvips 的解决方案。它应该是高质量的,而且很快。
它使 CIELAB 中的 L* 变亮。对于此类操作,这是一个不错的选择,因为 L* 将亮度分为 100 个刚好可见的差异级。 L* 范围的 20%、50% 和 80% 是阴影、中间调和高光位置的有用经验法则。
它是这样工作的:
- 图像已转换为 LABS 色彩空间。这是 CIELAB,但表示为带符号的短,因此 L* 为 0-32767,a* 和 b* 为 +/- 32767
- 它找到 L*(零)波段的直方图,并使用它来确定此图像的 1% 和 99% 点
- 然后将这些传递给
tonelut
,为生成的色调调整函数设置黑白。用户可以通过 command-line 参数 调整阴影、中间调和高光增强
tonelut
使用一组样条生成平滑单调的色调重映射函数- L*通过色调调整曲线映射
- 最后,它转换回原始色彩空间并写入
代码:
#!/usr/bin/python3
import sys
import pyvips
input_filename = sys.argv[1]
output_filename = sys.argv[2]
shadow_boost = float(sys.argv[3])
midtone_boost = float(sys.argv[4])
highlight_boost = float(sys.argv[5])
image = pyvips.Image.new_from_file(sys.argv[1])
# we work in LABS format, a 16-bit version of LAB, with 0 - 32767 for L*, and
# +/- 32767 for a* and b* +/- 128
original_interpretation = image.interpretation
image = image.colourspace("labs")
# find the histogram of L* and use it to pick the 1% and 99% points as black
# and white
hist = image[0].hist_find();
norm = hist.hist_cum().hist_norm() # normalised cumulative histogram
black = (norm > norm.width * 0.01).profile()[1].avg()
white = (norm > norm.width * 0.99).profile()[1].avg()
# tonelut wants percentages of 0-32767
black = 100.0 * black / 32767.0
white = 100.0 * white / 32767.0
# make a tone-mapping function using those black and white points
tone = pyvips.Image \
.tonelut(Lb=black, Lw=white, \
S=shadow_boost, M=midtone_boost, H=highlight_boost)
# remap L* of the image
image = (image[0].maplut(tone)).bandjoin(image[1:])
# back to the original interpretation, and save
image = image.colourspace(original_interpretation)
image.write_to_file(output_filename)
我可以运行这样:
$ ./tone.py ~/pics/girl.jpg x.jpg +10 +10 0
这也增强了中间调,因为那是皮肤掉落的地方。这是之前和之后:
我们可以从 better-quality 测试图像中提取更多信息。