The Shadows/Highlights command is a way to correct photos that are silhouetted by strong backlighting, or correct focus that is a little whitish due to being too close to the camera flash. In images lit in other ways, this adjustment can also be used to brighten shadowed areas. Instead of simply brightening or darkening an image, the Shadows/Highlights command brightens or darkens based on surrounding pixels (locally adjacent pixels) in the shadows or highlights. Because of this, shadows and highlights have their own controls. The default is set to fix images with backlighting issues, from Adobe's official website .
Shadow :
It is mainly used to brighten the shadow area, and has almost no effect on the non-shadow area
Amount : It is used to control the degree of correction of the shadow area, the larger the value, the brighter the shadow area is lifted
Tone : Control the brightened shadow area A smaller value will limit the brightening to only darker areas, a larger value will affect a wider range of shadows
Radius : Controls the size of local neighboring pixels around each pixel
Highlight:
It is mainly used to darken the highlight area, and has almost no effect on the non-highlight area
Amount : Used to control the degree of correction of the highlight area, the larger the value, the darker the highlight area is suppressed Tone
: Control the darkened highlight area A smaller value will limit the darkening to only brighter areas, a larger value will affect a wider range of highlights Radius
: Controls the size of local neighboring pixels around each pixel
Adjustment:
Color : Positive values increase the saturation of brightened pixels in the shadow area and darkened pixels in the highlight area, negative values decrease saturation
Midtone : used to adjust the contrast of the midtone, negative values reduce contrast, and positive values increase Contrast
Trim Black : Set how much of the shadows in the image are turned into pure black
Trim White : Set what proportion of the highlights in the image are turned into pure white
Python implements shadow area extraction and shadow area brightening
1. Extract the shadow area
Automatically advance the shadow area, for details, see my other blog "Python uses OpenCV to realize PS highlight/shadow selection"
Shadow area extraction:
first convert the original image into a grayscale image, then invert the grayscale image and use it as two input sources for orthogonal overlapping, and then output the result after thresholding
luminance = (1 - luminance) * (1 - luminance)
luminance = np.where(luminance > shadowThreshold, luminance, 0)
Then brighten the shadow area, and make a transition to the non-shadow, otherwise there will be a clear boundary.
The non-linear brightness adjustment used for brightening, the formula is the gamma correction formula, see the midtone adjustment in "PS Color Scale Adjustment Algorithm Formula Principle Detailed Explanation and Python Implementation" .
2. Brighten the shadow area and complete the code
Dependent environment: Python3 numpy OpenCV
# -*- coding: utf-8 -*-
# @Time : 2021-04-26 19:45
# @Author : AlanWang4523
# @FileName: ps_shadow_highlight.py
import os
import sys
import cv2
import numpy as np
class PSShadowHighlight:
"""
色阶调整
"""
def __init__(self, image):
self.shadows_light = 50
img = image.astype(np.float)/255.0
srcR = img[:, :, 2]
srcG = img[:, :, 1]
srcB = img[:, :, 0]
srcGray = 0.299 * srcR + 0.587 * srcG + 0.114 * srcB
# 高光选区
# luminance = luminance * luminance
# luminance = np.where(luminance > 0.64, luminance, 0)
# 阴影选区
luminance = (1 - srcGray) * (1 - srcGray)
self.maskThreshold = np.mean(luminance)
mask = luminance > self.maskThreshold
imgRow=np.size(img, 0)
imgCol=np.size(img, 1)
print("imgRow:%d, imgCol:%d, maskThreshold:%f" % (imgRow, imgCol, self.maskThreshold))
print("shape:", img.shape)
self.rgbMask = np.zeros([imgRow, imgCol, 3], dtype = bool)
self.rgbMask[:, :, 0] = self.rgbMask[:, :, 1] = self.rgbMask[:, :, 2] = mask
self.rgbLuminance = np.zeros([imgRow, imgCol, 3], dtype = float)
self.rgbLuminance[:, :, 0] = self.rgbLuminance[:, :, 1] = self.rgbLuminance[:, :, 2] = luminance
self.midtonesRate = np.zeros([imgRow, imgCol, 3], dtype = float)
self.brightnessRate = np.zeros([imgRow, imgCol, 3], dtype = float)
def adjust_image(self, img):
maxRate = 4
brightness = (self.shadows_light / 100.0 - 0.0001) / maxRate
midtones = 1 + maxRate * brightness
self.midtonesRate[self.rgbMask] = midtones
self.midtonesRate[~self.rgbMask] = (midtones - 1.0) / self.maskThreshold * self.rgbLuminance[~self.rgbMask] + 1.0
self.brightnessRate[self.rgbMask] = brightness
self.brightnessRate[~self.rgbMask] = (1 / self.maskThreshold * self.rgbLuminance[~self.rgbMask]) * brightness
outImg = 255 * np.power(img / 255.0, 1.0 / self.midtonesRate) * (1.0 / (1 - self.brightnessRate))
img = outImg
img[img < 0] = 0
img[img > 255] = 255
img = img.astype(np.uint8)
return img
def ps_shadow_highlight_adjust_and_save_img(psSH, origin_image):
psSH.shadows_light = 50
image = psSH.adjust_image(origin_image)
cv2.imwrite('py_sh_out_01.png', image)
def ps_shadow_highlight_adjust(path):
"""
阴影提亮调整
"""
origin_image = cv2.imread(path)
psSH = PSShadowHighlight(origin_image)
# ps_shadow_highlight_adjust_and_save_img(psSH, origin_image)
def update_shadows_light(x):
psSH.shadows_light = x
# 创建图片显示窗口
title = "ShadowHighlight"
cv2.namedWindow(title, cv2.WINDOW_NORMAL)
cv2.resizeWindow(title, 800, 600)
cv2.moveWindow(title, 0, 0)
# 创建阴影提亮操作窗口
option_title = "Option"
cv2.namedWindow(option_title, cv2.WINDOW_NORMAL)
cv2.resizeWindow(option_title, 400, 20)
cv2.moveWindow(option_title, 0, 630)
cv2.createTrackbar('shadows_light', option_title, psSH.shadows_light, 100, update_shadows_light)
while True:
image = psSH.adjust_image(origin_image)
cv2.imshow(title, image)
if cv2.waitKey(1) == ord('q'):
break
cv2.destroyAllWindows()
if __name__ == '__main__':
'''
运行环境:Python 3
执行:python3 ps_shadow_hightlight.py <图片路径>
如:python3 ps_shadow_hightlight.py test.jpg
'''
if len(sys.argv) == 1:
print("参数错误:未传入图片路径!")
sys.exit(-1)
img_path = sys.argv[1]
print("img_path Params:", img_path)
ps_shadow_highlight_adjust(img_path)
3. The final effect
The comparison between the default effect (degree of 50%) and the default effect of the shadow in PS:
the picture below is the comparison with the default effect of PS, the left is the default effect of PS (amount 30, hue 50%, radius 30, saturation +20), and the right is the above The default 50% effect achieved by Python is basically the same.
Effect pictures of other backlit pictures:
The following is the effect of other pictures implemented by Python. The left side is the original picture, and the right side is the processed picture. The default parameters are 50%. The graph is automatically calculated without manual modification):