Detailed explanation of the algorithm formula principle of PS color scale adjustment and Python implementation (color scale principle)

This article introduces the realization principle and formula of the color scale in PS, and implements it with Python. The self-test is basically the same as the color scale adjustment effect of PS (use the same parameters as the color scale in PS to compare the effect, including each limit value, this article only implements The processing of the RGB overall color scale, the processing logic formula for each channel is the same, and the actual use will be realized with OpenGL)

The figure below is the operation panel for color scale adjustment in PS, which can adjust the color scale of R, G, B individual channels or RGB overall channels. Each channel or the whole has five parameters (the five sliders pointed by the red arrows can also be directly input values ​​when adjusting):
inputShadows: The black threshold of the input image, the function is to make all the images below the threshold in the input image Become 0
inputHighlit: The white threshold of the input image, the function is to change all the input images above the threshold into 255
midtone: midtone, the range is [0.01, 9.99], the default value is 1.0 in the middle, [9.99 – 1.0 – 0.01], adjust from the middle to the left, that is, [1.0, 9.99] to add gray contrast, and adjust to the right to reduce gray and add contrast (RGB channel) outputShadows
: The black threshold of the output image, the lowest value of the output image is the threshold
outputHighlight : The white point threshold of the output image, the highest value of the output image is this threshold. The
insert image description here
processing conversion formula of the level adjustment is as follows:
There are three steps of input level mapping, midtone adjustment, and output level mapping. The output of the previous step is processed Input for the next step (the following formulas are edited with MarkDown Latex syntax).

Enter the color scale mapping formula:
V out = 255 ∗ V in − in Shadowsin Highlights − in Shadows Vout = 255 * \frac{Vin - inShadows}{inHighlights - inShadows}Vout=255inHighlightsinShadowsVininShadows
V o u t = { 0 if  V o u t < 0 255 if  V o u t > 255 Vout = \left\{ \begin{array}{ll} 0 & \textrm{if $Vout<0$}\\ 255 & \textrm{if $Vout>255$}\\ \end{array} \right. Vout={ 0255if Vout<0if Vout>255

picture name

Midtone adjustment:
V out = 255 ∗ ( V in 255.0 ) 1 midtones Vout = 255 * (\frac{Vin}{255.0})^\frac{1}{midtones}Vout=255(255.0Vin)I live here _ _ _1

picture name

输出色阶映射:
V o u t = V i n 255.0 ∗ ( o u t L i g h h i g h t s − o u t S h a d o w s ) + o u t S h a d o w s Vout = \frac{Vin}{255.0} * (outLighhights - outShadows) + outShadows Vout=255.0Vin(outLighhightsoutShadows)+outShadows

V o u t = { 0 if  V o u t < 0 255 if  V o u t > 255 Vout = \left\{ \begin{array}{ll} 0 & \textrm{if $Vout<0$}\\ 255 & \textrm{if $Vout>255$}\\ \end{array} \right. Vout={ 0255if Vout<0if Vout>255

picture name
The complete python code is as follows:
# -*- coding: utf-8 -*-
# @Time    : 2021-02-24 16:45
# @Author  : AlanWang4523
# @FileName: ps_levels.py

import os
import sys
import cv2
import numpy as np

class Levels:
    """
    @Author  : AlanWang4523
    色阶调整类,根据输入参数调整图片色阶,并输出处理后的图片
    """
    def __init__(self):
        self.channel = 0
        self.input_shadows = 0
        self.input_highlights = 255
        self.midtones = 1.0
        self.output_shadows = 0
        self.output_highlights = 255

    def adjust_image(self, img):
        print("Levels Params:")
        print("          channel:", self.channel)
        print("    input_shadows:", self.input_shadows)
        print(" input_highlights:", self.input_highlights)
        print("         midtones:", self.midtones)
        print("   output_shadows:", self.output_shadows)
        print("output_highlights:", self.output_highlights)
        print("")

        img = img.astype(np.float)

        # 输入色阶映射
        img = 255 * ((img - self.input_shadows) / (self.input_highlights - self.input_shadows))
        img[img < 0] = 0
        img[img > 255] = 255

        # 中间调处理
        img = 255 * np.power(img / 255.0, 1.0 / self.midtones)

        # 输出色阶映射
        img = (img / 255) * (self.output_highlights - self.output_shadows) + self.output_shadows
        img[img < 0] = 0
        img[img > 255] = 255

        img = img.astype(np.uint8)
        return img
                          
def level_adjust_and_save_img(origin_image):
    levels.input_shadows = 40
    levels.input_highlights = 240
    levels.midtones = 0.60
    levels.output_shadows = 30
    levels.output_highlights = 220

    image = levels.adjust_image(origin_image)

    cv2.imwrite('py_test_out.png', image)


def level_adjust(path):
    """
    色阶调整
    """
    origin_image = cv2.imread(path)

    levels = Levels()

    def update_input_shadows(x):
        if (x < levels.input_highlights):
            levels.input_shadows = x

    def update_input_highlights(x):
        if (x > levels.input_shadows):
            levels.input_highlights = x

    def update_midtones(x):
        # 由于 midtones 的调整范围是 [9.99, 0.01],Python 滑杆无法自定义显示小数,因此将滑杆的 [0, 100] 映射到 [9.99, 0.01]
        midtones = 1.0
        if (x < 50):
            midtones = 1 + 9 * ((50.0 - x) / 50.0)
        elif (x > 50):
            midtones = 1 - (x - 50) / 50.0
        
        levels.midtones = np.clip(midtones, 0.01, 9.99)
        # levels.midtones = 0.6 # 直接测试某个参数值

    def update_output_shadows(x):
        if (x < levels.output_highlights):
            levels.output_shadows = x

    def update_output_highlights(x):
        if (x > levels.output_shadows):
            levels.output_highlights = x
        
    # 创建图片显示窗口
    title = "Levels"
    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, 200)
    cv2.moveWindow(option_title, 800, 0)

    cv2.createTrackbar('    input_shadows', option_title, levels.input_shadows, 255, update_input_shadows)
    cv2.createTrackbar(' input_highlights', option_title, levels.input_highlights, 255, update_input_highlights)
    cv2.createTrackbar('         midtones', option_title, 50, 100, update_midtones)
    cv2.createTrackbar('   output_shadows', option_title, levels.output_shadows, 255, update_output_shadows)
    cv2.createTrackbar('output_highlights', option_title, levels.output_highlights, 255, update_output_highlights)

    while True:
        image = levels.adjust_image(origin_image)
        cv2.imshow(title, image)
        if cv2.waitKey(1) == ord('q'):
            break
    cv2.destroyAllWindows()        


if __name__ == '__main__':
    '''
    	Author: AlanWang4523
        运行环境:Python 3
        执行:python3 ps_levels.py <图片路径>
        如:python3 ps_levels.py test.jpg
    '''
    if len(sys.argv) == 1:
        print("参数错误:未传入图片路径!")
        sys.exit(-1)
    img_path = sys.argv[1]
    print("img_path Params:", img_path)
    level_adjust(img_path)
    def update_midtones(x):
        # 由于 midtones 的调整范围是 [9.99, 0.01],Python 滑杆无法自定义显示小数,因此将滑杆的 [0, 100] 映射到 [9.99, 0.01]
        midtones = 1.0
        if (x < 50):
            midtones = 1 + 9 * ((50.0 - x) / 50.0)
        elif (x > 50):
            midtones = 1 - (x - 50) / 50.0
        
        levels.midtones = np.clip(midtones, 0.01, 9.99)
        # levels.midtones = 0.6 # 直接测试某个参数值

Using the same parameters as PS, the effect is exactly the same as that on PS. The comparison effect with PS under each parameter is as follows:
(The upper part of the screenshot is the effect of using the same parameters as PS in Python, and the lower part is the effect of PS)
① Both Python and PS use the following parameters:
inputShadows: 50
inputHighlit: 52
midtone: 0.50 (according to the above mapping formula, the midtones slider implemented by python is at the position of 75)
outputShadows: 50
outputHighlight: 200
The effect is as follows:
insert image description here

② Both Python and PS use the following parameters:
inputShadows: 0
inputHighlit: 255
midtone: 0.10
outputShadows: 0
outputHighlight: 255
The effect is as follows:
insert image description here
Reference document: Levels Adjustment Algorithm for adjustment of image levels
on Adobe's official website

Guess you like

Origin blog.csdn.net/u011520181/article/details/114133219