简体   繁体   中英

Brighten only dark areas of image in python

I am trying to process images and I would like to brighten the dark areas of my image. I tried Histogram Equalization, however as there are also some bright areas in the image, the result is not satisfying. This is why I am looking for a way to brighten only the dark areas of the image.

As an example, Input image is on the left and the expected result is on the right, the hair and face of the girl are brightened

输入图像 增亮图像

ImageMagick seems to offer some possibilities to achieve this, however, I would like to do it using python

If you want to avoid colour distortions, you could:

  • convert to HSV colourspace,
  • split the channels,
  • bump up the V (Value) channel
  • recombine the channels
  • save

That might go something like this:

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

在此处输入图像描述

Essentially, I am changing the brightness from the black mapping to the green mapping:

在此处输入图像描述


Reminder to forgetful self... "you made the plot like this, Mark" :

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

Here is one way to do that in Imagemagick and Python/OpenCV. Threshold the L channel of LAB colorspace using triangle method. Then brighten the whole image. Then merge the original and brightened image using the threshold as a mask.

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

Threshold:

在此处输入图像描述

Result:

在此处输入图像描述

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

Threshold:

在此处输入图像描述

Result:

在此处输入图像描述

An alternate way to do this in Imagemagick is to use -fx to threshold and increase brightness using the L channel of LAB or the Y channel of YCbCr or the V channel of HSV by adding a constant value where that channel is less than some threshold.

(Note: In -fx, images such as u vary from 0 to 1 in range.)

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

LAB Result:

在此处输入图像描述

YCbCr Result:

在此处输入图像描述

HSV Result:

在此处输入图像描述

Similar processing can be done using Python/OpenCV as follows:

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

Result:

在此处输入图像描述

Here is how you can accomplish that with just the cv2 module:

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)

Input Image and Output Image:

在此处输入图像描述 在此处输入图像描述

Here's a solution using the tonelut operation in pyvips . It should be high quality, and pretty quick.

It brightens L* in CIELAB. This is a good choice for this sort of operation, since L* divides brightness into 100 steps of just visible difference. 20%, 50% and 80% of the L* range are useful rule of thumb positions for the shadows, midtones and highlights.

It works like this:

  • The image is converted to LABS colourspace. This is CIELAB, but represented as signed short, so 0-32767 for L* and +/- 32767 for a* and b*
  • It finds the histogram of the L* (zero) band, and uses that to determine the 1% and 99% points of this image
  • These are then passed to tonelut to set black and white for the generated tone adjustment function. The user can adjust the shadow, midtone and highlight boosts via command-line arguments
  • tonelut uses a set of splines to generate a smooth and monotonic tone remapping function
  • L* is mapped through the tone adjustment curve
  • Finally, it converts back to the original colourspace and writes

Code:

#!/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)

I can run it like this:

$ ./tone.py ~/pics/girl.jpg x.jpg +10 +10 0

That's boosting the midtones as well, since that's where the skin falls. Here's a before and after:

之前和之后

We'd be able to pull more out of a better-quality test image.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM