本文介绍了 PS 中色阶的实现原理及公式,并用 Python 实现,自测与 PS 的色阶调整效果基本完全一样(使用和 PS 中色阶相同的参数对比效果,包括各极限值,本文只实现了 RGB 整体色阶的处理,对各个通道的处理逻辑公式是一样的,实际使用会用 OpenGL 实现)

如下图是 PS 中色阶调整的操作面板,可以对 R、G、B 单独的通道或 RGB 整体通道做色阶调整。每个通道或整体有五个参数(红色箭头所指的五个滑块,调整时也可以直接输入值):
inputShadows: 输入图像的黑场阈值,作用是将输入图像中低于该阈值的全变成 0
inputHighlit: 输入图像的白场阈值,作用是将输入图像中高于该阈值的全变成 255
midtone: 中间调,范围是 [0.01, 9.99],默认值是 1.0 在中间,[9.99 – 1.0 – 0.01],从中间往左调,即[1.0, 9.99] 加灰降对比度,往右调减灰加对比度(RGB通道)
outputShadows: 输出图像的黑场阈值,输出图像的最低值为该阈值
outputHighlight: 输出图像的白场阈值,输出图像的最高值为该阈值

色阶调整的处理转换公式如下:
分输入色阶映射、中间调调整、输出色阶映射共三步处理,上一步处理的输出做为下一步的输入(以下公式用 MarkDown Latex 语法编辑)。

输入色阶映射公式:
Vout=255∗Vin−inShadowsinHighlights−inShadowsVout = 255 * \frac{Vin - inShadows}{inHighlights - inShadows} Vout=255∗inHighlights−inShadowsVin−inShadows​
Vout={0if Vout<0255if Vout>255Vout = \left\{ \begin{array}{ll} 0 & \textrm{if $Vout<0$}\\ 255 & \textrm{if $Vout>255$}\\ \end{array} \right. Vout={0255​if Vout<0if Vout>255​

中间调调整:
Vout=255∗(Vin255.0)1midtonesVout = 255 * (\frac{Vin}{255.0})^\frac{1}{midtones} Vout=255∗(255.0Vin​)midtones1​

输出色阶映射:
Vout=Vin255.0∗(outLighhights−outShadows)+outShadowsVout = \frac{Vin}{255.0} * (outLighhights - outShadows) + outShadows Vout=255.0Vin​∗(outLighhights−outShadows)+outShadows

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

完整 python 代码如下:

# -*- coding: utf-8 -*-
# @Time    : 2021-02-24 16:45
# @Author  : AlanWang4523
# @FileName: ps_levels.pyimport os
import sys
import cv2
import numpy as npclass Levels:"""@Author  : AlanWang4523色阶调整类,根据输入参数调整图片色阶,并输出处理后的图片"""def __init__(self):self.channel = 0self.input_shadows = 0self.input_highlights = 255self.midtones = 1.0self.output_shadows = 0self.output_highlights = 255def 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] = 0img[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_shadowsimg[img < 0] = 0img[img > 255] = 255img = img.astype(np.uint8)return imgdef level_adjust_and_save_img(origin_image):levels.input_shadows = 40levels.input_highlights = 240levels.midtones = 0.60levels.output_shadows = 30levels.output_highlights = 220image = 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 = xdef update_input_highlights(x):if (x > levels.input_shadows):levels.input_highlights = xdef update_midtones(x):# 由于 midtones 的调整范围是 [9.99, 0.01],Python 滑杆无法自定义显示小数,因此将滑杆的 [0, 100] 映射到 [9.99, 0.01]midtones = 1.0if (x < 50):midtones = 1 + 9 * ((50.0 - x) / 50.0)elif (x > 50):midtones = 1 - (x - 50) / 50.0levels.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 = xdef 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'):breakcv2.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.0if (x < 50):midtones = 1 + 9 * ((50.0 - x) / 50.0)elif (x > 50):midtones = 1 - (x - 50) / 50.0levels.midtones = np.clip(midtones, 0.01, 9.99)# levels.midtones = 0.6 # 直接测试某个参数值

使用和 PS 相同的参数,效果和 PS 上的完全一致,各参数下和 PS 对比效果如下:
(截图上半部部分为用 Python 使用与 PS 相同参数的效果,下半部分为 PS 的效果)
① Python 和 PS 都使用如下参数:
inputShadows: 50
inputHighlit: 52
midtone: 0.50 (根据上面的映射公式,python 实现的 midtones 的滑块在 75 的位置)
outputShadows: 50
outputHighlight: 200
效果如下:

② Python 和 PS 都使用如下参数:
inputShadows: 0
inputHighlit: 255
midtone: 0.10
outputShadows: 0
outputHighlight: 255
效果如下:

参考文档:
Adobe 官网的 Levels Adjustment
Algorithm for adjustment of image levels

PS 色阶调整之算法公式原理详解及 Python 实现(色阶原理)相关推荐

  1. 视频教程-深度学习原理详解及Python代码实现-深度学习

    深度学习原理详解及Python代码实现 大学教授,美国归国博士.博士生导师:人工智能公司专家顾问:长期从事人工智能.物联网.大数据研究:已发表学术论文100多篇,授权发明专利10多项 白勇 ¥88.0 ...

  2. java源码系列:HashMap底层存储原理详解——4、技术本质-原理过程-算法-取模具体解决什么问题

    目录 简介 取模具体解决什么问题? 通过数组特性,推导ascii码计算出来的下标值,创建数组非常占用空间 取模,可保证下标,在HashMap默认创建下标之内 简介 上一篇文章,我们讲到 哈希算法.哈希 ...

  3. google authenticator python_Google Authenticator TOTP原理详解(以Python为例)

    http://xsboke.blog.51cto.com 如果有疑问,请点击此处,然后发表评论交流,作者会及时回复(也可以直接在当前文章评论). -------谢谢您的参考,如有疑问,欢迎交流 一. ...

  4. 字典树原理详解及其Python实现

    一.原理详解 1.初步介绍: 字典树又名前缀树,Trie树,是一种存储大量字符串的树形数据结构,经常被搜索引擎系统用于文本词频统计. 除此之外也常用于计算左右信息熵.计算点互信息. 下图演示了一个保存 ...

  5. python google auth totp_Google Authenticator TOTP原理详解(以Python为例)

    如果有疑问,请点击此处,然后发表评论交流,作者会及时回复(也可以直接在当前文章评论). -------谢谢您的参考,如有疑问,欢迎交流 一. 原理详解(图片可以点击然后放大查看) 二. 验证 1.下载 ...

  6. Xgboost算法原理详解及python实现

    Xgboost算法(回归树) 1.算法原理 2.对数据的要求(无需规范化) 3.算法的优缺点 4.XGB.GBDT.LR与RF 5.python代码实现 导入相关包 读取数据并预处理 训练 贝叶斯初步 ...

  7. 随机森林原理详解及python代码实现

    随机森林(RF)算法 1.算法原理 2.对数据的要求(无需规范化) 3.算法的优缺点 4.算法需要注意的点 5.python代码实现(待更......) 导入相关包 读取数据并预处理(必须处理缺失值) ...

  8. 决策树原理详解及python代码实现

    决策树算法(信贷中常用来寻找规则) 1.算法原理 1.1 ID3(多叉树分类) 1.2 C4.5(多叉树分类) 1.3 Cart(二叉树分类+回归) 2.ID3.C4.5与Cart比较 3.算法优缺点 ...

  9. 集成经验模态(EEMD)原理详解与python实现

    文章目录 1 经验模态分解(EMD) 1.1 本征模态函数(IMF) 1.2 sifting算法 1.3 原始序列重构 2 集成经验模态分解(EEMD) 2.1 EEMD算法步骤 集成经验模态分解的关 ...

最新文章

  1. 从时间管理聊到技术人如何保持竞争力?
  2. mysql索引与优化
  3. python知识:@classmethod和@staticmethod的异同
  4. python nonetype object has no_为什么会出现这个?'NoneType' object has no attribute 'contet
  5. Draconian,自由或保姆状态:Java,C#,C,C ++,Go和Rust中的并发意识形态
  6. mysql 左连接 和全连接_mysql左连接,右连接,内连,全连
  7. java在线聊天项目 使用SWT快速制作登录窗口,可视化窗口Design 更换窗口默认皮肤(切换Swing自带的几种皮肤如矩形带圆角)...
  8. mysql for 循环删除_Java增强for循环中删除元素抛异常问题
  9. 2021年中国船用蓄电池市场趋势报告、技术动态创新及2027年市场预测
  10. linux 重启mysql_Grafana+Prometheus 监控 MySql服务
  11. Pytorch测试模型的GFLOPs和Param大小
  12. 修改oracle用户密码永不过期的方法
  13. 使用学信网认证,免费获取JetBrains学习产品
  14. 用行列式的定义方法求解n阶行列式的值(C++)
  15. 生鲜配送系统软件排名
  16. knex 找不到mysql_node knex mysql ER_NOT_SUPPORTED_AUTH_MODE
  17. electron 自动更新 热跟新
  18. 如何自创一门计算机语言
  19. GitHub 热点速览 Vol.32:VScode 韭菜基金插件,极大提高“工作”效率
  20. 【人工智能毕设之基于Python+flask+bilstm的评论情感分析系统-哔哩哔哩】 https://b23.tv/QU56eTl

热门文章

  1. 关于Vue中nextTick异步调用videoaudio的方法失效解决方案
  2. Windows系统下使用pyinstaller打包PaddleOCR中表格识别PP-Structure
  3. Mac 电脑能联网但打不开网页
  4. Nginx下载安装以及简单使用
  5. html 怎么播放avi视频,Uniboy跳舞激怒Kanavi,JDG让一追二,艰难战胜V5
  6. Evil Coordinate
  7. 应用案例 | 2012 款大众途观车怠速抖动、加速无力故障诊断
  8. ios运行.php,ios - 真机运行报错 怎么解决 !!!
  9. Pytorch中.new()的作用
  10. 挂脖式运动蓝牙耳机什么牌子的好、运动蓝牙挂脖耳机推荐