在其他文章中,已经有了最基本的冷暖色调转换方案,比如单纯根据level值加减R,B分量,
python实现图像的白平衡,破坏图像的白平衡(冷、暖)和调节图像的亮度_python 根据照片求色温-优快云博客
或者高级一些,定量计算某个色温下黑体辐射的RGB值,即白点RGB坐标:
色温(Kelvin)到RGB的转换:算法和样例_色温转rgb-优快云博客
但这篇文章有一个问题,按照给出的代码只能画出不同色温下白点的颜色,没有说明实际图像中各个像素的rgb值是怎么变化的,即怎么做整个图像色温变换。根据原文链接,我们可以找到这部分代码对应的PhotoDemon软件开源源码:
逐像素调节色温的核心代码在 Forms/Adjustments_Color_Temperature.frm 295行附近,
其中If (temperatureMethod = PDTA_Basic) 分支直接对应“单纯根据level值加减R,B分量”的简化方案;else分支对应的是改进后方案,即计算出白点的tmpR,tmpG,tmpB后和原始像素混合。Colors.BlendColors(r, tmpR, tempStrength)是颜色混合函数,具体定义可以在Modules/Colors.bas中找到。
至于为什么是像素RGB和白点RGB值混合,我想是利用两个颜色向量插值,来实现原始色彩向目标场色彩偏移。
For y = initY To finalY
workingDIB.WrapArrayAroundScanline imageData, tmpSA1D, y
For x = initX To finalX Step 4
'Get the source pixel color values
b = imageData(x)
g = imageData(x + 1)
r = imageData(x + 2)
'If luminance is being preserved, we need to determine the initial luminance value
originalLuminance = Colors.GetLuminance(r, g, b) * ONE_DIV_255
If (temperatureMethod = PDTA_Basic) Then
Colors.ImpreciseRGBtoHSL rLookup(r), g, bLookup(b), h, s, l
Colors.ImpreciseHSLtoRGB h, s, originalLuminance, r, g, b
Else
'Blend the original and new RGB values using the specified strength
r = Colors.BlendColors(r, tmpR, tempStrength)
g = Colors.BlendColors(g, tmpG, tempStrength)
b = Colors.BlendColors(b, tmpB, tempStrength)
'If the user wants us to preserve luminance, determine the hue and saturation of the new color, then replace the luminance
' value with the original
If preserveLuminance Then
Colors.ImpreciseRGBtoHSL r, g, b, h, s, l
Colors.ImpreciseHSLtoRGB h, s, originalLuminance, r, g, b
End If
End If
'Assign the new values to each color channel
imageData(x) = b
imageData(x + 1) = g
imageData(x + 2) = r
总结
采用基于白点的色温转换(色温(Kelvin)到RGB的转换:算法和样例_色温转rgb-优快云博客),需要注意算出白点后和原始像素线性加权混合,才能完成实际图像的色彩转换.
或者,按照原评论区所讲述的方案,将目标色温下的RGB作为最大值归一化。即R1=R/R_temp*255,也是可以的。这种方法的背后原理是色适应变换(CAT),更深入可以了解CIE CAM02中的CAT02算法和实现。
附录:python实现
def get_rgb_from_temperature(temp):
"""
根据色温 (Kelvin) 计算白点的 RGB 值
:param temp: 色温 (Kelvin),通常范围在 1000K - 40000K
:return: 对应色温的 RGB (r, g, b) 值
"""
# 限制色温的范围
temp = max(1000, min(temp, 40000)) / 100.0 # 将色温范围限制在1000K到40000K之间,并转换为百位制
# 初始化 RGB
r, g, b = 0, 0, 0
# 计算红色分量
if temp <= 66:
r = 255
else:
tmpCalc = temp - 55
r = 351.976905668057 + 0.114206453784165 * tmpCalc + -40.2536630933213 * np.log(tmpCalc)
r = min(255, max(0, r)) # 限制在0-255之间
# 计算绿色分量
if temp <= 66:
tmpCalc = temp-2
g = -155.254855627092 + -0.445969504695791 * tmpCalc + 104.492161993939 * np.log(tmpCalc)
g = min(255, max(0, g)) # 限制在0-255之间
else:
tmpCalc = temp - 50
g = 325.449412571197 + 7.94345653666234E-02 * tmpCalc + -28.0852963507957 * np.log(tmpCalc)
g = min(255, max(0, g)) # 限制在0-255之间
# 计算蓝色分量
if temp >= 66:
b = 255
else:
if temp <= 19:
b = 0
else:
tmpCalc = temp - 10
b = -254.769351841209 + 0.827409606400739 * tmpCalc + 115.679944010661 * np.log(tmpCalc)
b = min(255, max(0, b)) # 限制在0-255之间
return np.array([r,g,b],dtype=np.uint8).reshape(1,1,3)
# 示例调用
temp = 6500 # 色温 (Kelvin)
rgb = get_rgb_from_temperature(temp)
r = rgb[...,0]
g = rgb[...,1]
b = rgb[...,2]
print(f"色温 {temp}K 的 RGB 值是: R={r}, G={g}, B={b}")
# show color bar
img_array = np.zeros((20,150-15,3),dtype=np.uint8)
for i in range(0,150-15):
rgb_temp = get_rgb_from_temperature((15+i)*100)
img_array[:,i,:] = rgb_temp
plt.imshow(img_array)
#---- 对一张图做色彩变换 ----#
def blend_color(img_ori,white_point,alpha=0.8):
'''
Color fusion.
Out = alpha*img_ori+(1-alpha)*white_point
'''
img_dst = alpha*img_ori+(1-alpha)*white_point
img_dst[img_dst>255] = 255
img_dst = np.uint8(img_dst)
return img_dst
img_ori = ... # 注意要求RGB顺序
img_dst = blend_color(img_ori,get_rgb_from_temperature(8000))
plt.imshow(img_dst)
# 或采用色适应假设:以的白点归一化. 这种方案不会显得图片偏白
white_point = get_rgb_from_temperature(8000)
img_dst = img_ori/white_point*255.
img_dst[img_dst>255] = 255
img_dst = np.uint8(img_dst)
plt.imshow(img_dst)