本文介绍了 PS 中色阶的实现原理及公式,并用 Python 实现,自测与 PS 的色阶调整效果基本完全一样。
PS 中色彩平衡可以对高光、中间调、阴影 三个色调进行调整,每个色调中有可以对 RGB 三个通道调整,每个通道的调整区间范围是 [-100, 100]

色彩平衡之高光

高光各色条的现象及规律如下:

  • a. 对于正向的调整,只增加输入图像中该通道的值,其他两个通道不变,比如只调整蓝色 +100,那么原图中蓝色通道值会增加,红色通道和绿色通道不变,如下图;
  • b. 对于负向的调整,该通道值不变,其他两个通道值增加,比如同样只调整蓝色 -100,那么原图中蓝色通道值不变,红色通道和绿色通道值增加,如下图;
  • c. 此外如果将三个调整滑杆都拖动相同的数值,那么原图没有变化。如 RGB 三个滑杆值每个通道都加或减相同数值,效果一样,如 (+10, +20, +30)的效果等价于 (-10, 0, 10) 的效果(RGB 三个滑杆都减 20),也等价于 (20, 30, 40) 的效果(RGB 三个滑杆都加 10)可通过 PS 验证。
  • d. 生成一张 255x255 的图,其从左到右每个像素点 RGB 每个通道的颜色值从 0~255,然后将该图通过 PS 做色彩平衡处理并存处理后的图片,下图中横轴是取的 0~255,纵轴是取的通过 PS 处理后的 RGB 各个通道的值并画线
  • e. 通过设置不同的参数值导出多张图,然后拟合出调整系数(上图中变化直线斜率)与滑杆调整值[0, 100]的关系(见上图高光下加不同蓝色值对应的输出曲线),只测正值即可,负值相当于其另外两个通道做正向调整(公式如下,Vin 为输入图像的 RGB,a 为固定系数,X 为各通道的正向调节值 [0, 100];

色彩平衡-高光映射公式:
Vout=255∗Vin255−XVout = \frac{255 * Vin}{255 - X} Vout=255−X255∗Vin​

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​

色彩平衡之阴影调整

阴影各色条的现象及规律如下:

  • a. 对于正向的调整,该通道值不变,其他两个通道值减小,比如同样只调整蓝色 +100,那么原图中蓝色通道值不变,红色通道和绿色通道值减小,如下图;
  • b. 对于负向的调整,只减少输入图像中该通道的值,其他两个通道不变,比如只调整蓝色 -100,那么原图中蓝色通道值会减小,红色通道和绿色通道不变,如下图;
  • c. 此外如果将三个调整滑杆都拖动相同的数值,那么原图没有变化。如果 RGB 三个滑杆值每个通道都加或减相同数值,效果一样。
  • d. 生成一张 255x255 的图,其从左到右每个像素点 RGB 每个通道的颜色值从 0~255,然后将该图通过 PS 做色彩平衡处理并存处理后的图片,下图中横轴是取的 0~255,纵轴是取的通过 PS 处理后的 RGB 各个通道的值并画线
  • e. 通过设置不同的参数值导出多张图,然后拟合出直线斜率、截距与滑杆调整值[0, 100]的关系(见上图阴影下加不同蓝色值对应的输出曲线),只测负值即可,正值相当于其另外两个通道做负向调整(公式如下,Vin 为输入图像的 RGB,X 为各通道的正向调节值 [0, 100])

色彩平衡-阴影映射公式:
Vout=255∗(Vin−X)255−XVout = \frac{255 * (Vin - X)}{255 - X} Vout=255−X255∗(Vin−X)​

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​

代码如下:

# -*- coding: utf-8 -*-
# @Time    : 2021-03-09 11:05
# @Author  : AlanWang4523
# @FileName: ps_color_balance_test.pyimport os
import sys
import cv2
import numpy as npTONE_SHADOWS   = 0
TONE_MIDTONE   = 1
TONE_HIGHLIGHT = 2
SH_COEFFICIENTS = 0.0039216 # 1.0/255
MID_COEFFICIENTS = 0.0033944class ColorBalance:"""色彩平衡调整"""def __init__(self):self.tone = TONE_SHADOWSself.tone_arguments = np.zeros([3,3], dtype=np.int)self.tone_coefficients = np.zeros([3,3])self.sh_coef_self = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])self.sh_coef_other = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]])self.mid_coef_self = np.array([[1, -1, -1], [-1, 1, -1], [-1, -1, 1]])def update_tones(self, x):self.tone = x;def update_cyan_red(self, x):self.tone_arguments[self.tone, 0] = xdef update_magenta_green(self, x):self.tone_arguments[self.tone, 1] = xdef update_yellow_blue(self, x):self.tone_arguments[self.tone, 2] = xdef adjust_image(self, img):print("ColorBalance Params:")print("   Shadows: ", self.tone_arguments[TONE_SHADOWS][:])print("  Midtones: ", self.tone_arguments[TONE_MIDTONE][:])print("Highlights: ", self.tone_arguments[TONE_HIGHLIGHT][:])# print("   Shadows: %d, %d, %d" % (self.tone_arguments[TONE_SHADOWS, 0], self.tone_arguments[TONE_SHADOWS, 1], self.tone_arguments[TONE_SHADOWS, 2]))# print("  Midtones: %d, %d, %d" % (self.tone_arguments[TONE_MIDTONE, 0], self.tone_arguments[TONE_MIDTONE, 1], self.tone_arguments[TONE_MIDTONE, 2]))# print("Highlights: %d, %d, %d" % (self.tone_arguments[TONE_HIGHLIGHT, 0], self.tone_arguments[TONE_HIGHLIGHT, 1], self.tone_arguments[TONE_HIGHLIGHT, 2]))temp = np.sort(self.tone_arguments[TONE_SHADOWS][:])self.tone_coefficients[TONE_SHADOWS][:] = (self.tone_arguments[TONE_SHADOWS][:] - temp[1]) * SH_COEFFICIENTStemp = np.sort(self.tone_arguments[TONE_MIDTONE][:])self.tone_coefficients[TONE_MIDTONE][:] = (self.tone_arguments[TONE_MIDTONE][:] - temp[1]) * MID_COEFFICIENTStemp = np.sort(self.tone_arguments[TONE_HIGHLIGHT][:])self.tone_coefficients[TONE_HIGHLIGHT][:] = (self.tone_arguments[TONE_HIGHLIGHT][:] - temp[1]) * SH_COEFFICIENTSprint("tone_coefficients:")print("   Shadows: ", self.tone_coefficients[TONE_SHADOWS][:])print("  Midtones: ", self.tone_coefficients[TONE_MIDTONE][:])print("Highlights: ", self.tone_coefficients[TONE_HIGHLIGHT][:])test_array = self.tone_coefficients[TONE_HIGHLIGHT][:]self.tone_coefficients[TONE_HIGHLIGHT][:] = np.dot(np.where(test_array > 0, test_array, 0), self.sh_coef_self) +\np.dot((-1) * np.where(test_array < 0, test_array, 0), self.sh_coef_other)test_array = self.tone_coefficients[TONE_SHADOWS][:]self.tone_coefficients[TONE_SHADOWS][:] = np.dot(np.where(test_array > 0, test_array, 0), self.sh_coef_other) +\np.dot((-1) * np.where(test_array < 0, test_array, 0), self.sh_coef_self)  # test_array = self.tone_coefficients[TONE_MIDTONE][:]# self.tone_coefficients[TONE_MIDTONE][:] = np.dot(test_array, self.mid_coef_self)# self.tone_coefficients[TONE_MIDTONE][:] = np.power(e, np.dot(test_array, self.mid_coef_self))print("after_tone_coefficients:")print("   Shadows: ", self.tone_coefficients[TONE_SHADOWS][:])print("  Midtones: ", self.tone_coefficients[TONE_MIDTONE][:])print("Highlights: ", self.tone_coefficients[TONE_HIGHLIGHT][:])# r, g, b = img = img.astype(np.float)b, g ,r =cv2.split(img)r = r / (1.0 - self.tone_coefficients[TONE_HIGHLIGHT, 0])g = g / (1.0 - self.tone_coefficients[TONE_HIGHLIGHT, 1])b = b / (1.0 - self.tone_coefficients[TONE_HIGHLIGHT, 2])r[r > 255] = 255g[g > 255] = 255b[b > 255] = 255r = (r - 255.0 * self.tone_coefficients[TONE_SHADOWS, 0]) / (1.0 - self.tone_coefficients[TONE_SHADOWS, 0])g = (g - 255.0 * self.tone_coefficients[TONE_SHADOWS, 1]) / (1.0 - self.tone_coefficients[TONE_SHADOWS, 1])b = (b - 255.0 * self.tone_coefficients[TONE_SHADOWS, 2]) / (1.0 - self.tone_coefficients[TONE_SHADOWS, 2])r[r < 0] = 0g[g < 0] = 0b[b < 0] = 0img = cv2.merge([b, g, r])img = img.astype(np.uint8)return img    def color_balance_adjust(path):"""色彩平衡调整"""origin_image = cv2.imread(path)cloorBalance = ColorBalance()title = "ColorBalance"# 阴影cloorBalance.update_tones(0)cloorBalance.update_cyan_red(0)cloorBalance.update_magenta_green(-100)cloorBalance.update_yellow_blue(0)# 中间调cloorBalance.update_tones(1)cloorBalance.update_cyan_red(-30)cloorBalance.update_magenta_green(-50)cloorBalance.update_yellow_blue(-50)# 高光cloorBalance.update_tones(2)cloorBalance.update_cyan_red(100)cloorBalance.update_magenta_green(0)cloorBalance.update_yellow_blue(0)image = cloorBalance.adjust_image(origin_image)    cv2.namedWindow(title, cv2.WINDOW_NORMAL)   cv2.resizeWindow(title, 800, 600)cv2.moveWindow(title, 0, 0)while True:cv2.imshow(title, image)if cv2.waitKey(1) == ord('q'):breakcv2.destroyAllWindows()        if __name__ == '__main__':'''运行环境:Python 3执行:python3 ps_color_balance_test.py <图片路径>如:python3 ps_color_balance_test.py test.jpg'''if len(sys.argv) == 1:print("参数错误:未传入图片路径!")sys.exit(-1)img_path = sys.argv[1]print("img_path Params:", img_path)color_balance_adjust(img_path)

感谢:
https://zhuanlan.zhihu.com/p/59450298

PS 色彩平衡之算法公式原理详解及 Python 实现相关推荐

  1. PS 色阶调整之算法公式原理详解及 Python 实现(色阶原理)

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

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

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

  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算法步骤 集成经验模态分解的关 ...

  10. KNN算法原理详解及python代码实现

    KNN算法 算法原理 对数据的要求 算法的优缺点 算法需要注意的点 算法实现(python) 算法原理 计算待测样本与train_data的距离d并保存数组中 对d进行排序,取d最近的k个样本 统计样 ...

最新文章

  1. 学习Python往哪个方向发展好
  2. localdate转date时区问题_时间戳和LocalDateTime和Date互转和格式化
  3. Oracle中的Union、Union All、Intersect、Minus 使用用法区别
  4. 一个线性几何不等式猜想
  5. Nova计算节点安装配置
  6. Note for Consulting Handbook5
  7. linux文件编程(1)—— open、write、read、lseek、阻塞问题
  8. Avalonia跨平台入门第四篇之Popup在uos下问题
  9. linux path减少,Linux的环境变量PATH中所带来的问题及解决方法
  10. 使用路标的Scala和Java的Twitter REST API
  11. 中yeti不能加载_第二十章_类的加载过程详解
  12. MFC中CString.Format的用法
  13. 知道这些用于数据科学和机器学习的GitHub存储库和Reddit主题吗?
  14. 细谈最近上线的Vue2.0项目(一)
  15. binder 从c到java_Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析...
  16. Python的单链表实现
  17. Apache Maven --- [标准目录布局]
  18. 鸿蒙系统桌面天气如何设置,怎么设置桌面时间和天气预报?
  19. 曹金明:Zynga大败局--数据控是如何把游戏做败的
  20. 网易云信服务监控平台实践

热门文章

  1. 20年研发管理经验谈(二)
  2. Hadoop3.3.1 踩坑笔记
  3. Nuxt SSR 部署实践
  4. wap_supplicant介绍
  5. 数学建模:R语言的正态性检验
  6. FreeCAD 乐高积木
  7. python音频 降噪_一种基于深度神经网络的音频降噪方法技术
  8. 触发器的三种触发方式:电平触发、边沿触发、脉冲触发区别
  9. 硕士论文查重原理是什么?
  10. java 幸运大转盘_幸运大转盘抽奖 抽奖算法 程序实现逻辑