03_tonemapping
原始文件为 .py 代码,本文是转换后的 Markdown 文件。
```.py
import os
os.chdir(os.path.dirname(os.path.abspath(file)))
import numpy as np
from func.cp_hw2 import readHDR, writeHDR
from func.cp_hw2 import lRGB2XYZ, XYZ2lRGB
def XYZ2xyY(XYZ):
X, Y, Z = np.split(XYZ, 3, axis=2)
x = X / (X + Y + Z)
y = Y / (X + Y + Z)
return np.dstack((x, y, Y))
def xyY2XYZ(xyY):
x, y, Y = np.split(xyY, 3, axis=2)
X = x * Y / y
Z = (1 - x - y) * Y / y
return np.dstack((X, Y, Z))
def ACES_Tonemapping(img):
img = 0.6 * img
a, b, c, d, e = 2.51, 0.03, 2.43, 0.59, 0.14
return (img * (a * img + b)) / (img * (c * img + d) + e)
def Reinhard_Tonemapping(img):
return (img) / (1 + img)
def Tonemapping(I, method):
# 找到中间值,然后映射为一个灰点,之后根据它先把原图像压缩到一定程度的大小,这样效果更好
# 转换前后的最大值举例: 1568->202,4064->477
# 这里将灰点映射为 0.18,所谓的 十八度灰
Imid = np.exp(np.sum(np.log(I[I!=0])) / (rows*cols))
Iwave = I * (0.18 / Imid)
# 这里的步骤计算一个放缩函数,三个点
# 1. 原因:不放缩,像素值较大的地方利用的较少
# 比如 Imax=1,那么不放缩,最后的结果为 Imax/(1+Imax)=0.5,这肯定不好
# 如果 Imax 足够大,那就不用放缩了,比如 Imax=500,转换后max也比较接近1了
# 2. 这个放缩函数也很巧妙,不是所有像素乘以固定值,也是一个非线性函数
# 像素越大,乘以的值越大,和出发点:像素值较大地方利用较少,比较一致
# 3. 不是单纯使用 max,而是有一定系数调整(Iwhite)
Iwhite = 0.98 * np.max(Iwave)
Iscale = 1 + Iwave / (Iwhite * Iwhite)
# 返回转换后的结果
if method == 'Reinhard':
return Reinhard_Tonemapping(Iwave) * Iscale
elif method == 'ACES':
return ACES_Tonemapping(Iwave) * Iscale
return None
srcdir = '../result/stage-2'
dstdir = '../result/stage-3'
os.makedirs(dstdir, exist_ok=True)
for filename in os.listdir(srcdir):
print(filename)
hdrimg = readHDR(f'{srcdir}/{filename}')
rows, cols = hdrimg.shape[0:2]
# 三个通道分别进行处理
result = np.zeros_like(hdrimg)
for channel in range(0, 3):
I = hdrimg[..., channel]
result[..., channel] = Tonemapping(I, method='Reinhard')
# result[..., channel] = Tonemapping(I, method='ACES')
result = ((result.clip(0, 1) ** 0.45) * 255).astype(np.uint8)
writeHDR(f'{dstdir}/{filename}-RGB1.png', result)
# 对亮度进行处理
# Lold = 0.2126*hdrimg[..., 0] + 0.7152*hdrimg[..., 1] + 0.0722*hdrimg[..., 2]
xyY = XYZ2xyY(lRGB2XYZ(hdrimg))
xyY[..., 2] = Tonemapping(xyY[..., 2], method='Reinhard')
result = XYZ2lRGB(xyY2XYZ(xyY))
result = ((result.clip(0, 1) ** 0.45) * 255).astype(np.uint8)
writeHDR(f'{dstdir}/{filename}-RGB2.png', result)```