如何使用OpenCV对32位EXR文件执行线性转sRGB的Gamma校正
32位EXR线性到sRGB转换的Gamma校正实现
首先解释你现有代码里65535的含义:
- 65535是16位无符号整数的最大可存储值,你现有代码只是把EXR里的线性浮点值硬乘系数截断后存为16位PNG,全程没做sRGB Gamma编码,输出发暗发灰是必然结果。
- 32位EXR存储的是线性光空间的HDR浮点值,数值没有上限,直接线性缩放存成整数图片格式,不会自动适配sRGB的显示曲线,自然和预期显示效果差距很大。
可直接运行的修正代码
import os os.environ['OPENCV_IO_ENABLE_OPENEXR'] = 'True' import numpy as np import cv2 # 读取EXR原始数据,-1参数保留原始位深不做自动转换 im = cv2.imread(r"D:\CG_CONTENT\HDRIS\HDRI_BROWSER\Concrete_Office_Outside_sm.exr", -1) im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB) # 1. HDR转LDR色调映射:EXR亮度值可远超1.0,先压缩到0-1区间 # 这里用最通用的Reinhard全局映射,有更高要求可换OpenCV自带的其他Tonemap算法 im = im / (1 + im) # 2. 核心:线性空间转sRGB的标准分段Gamma校正(不要直接用1/2.2次方近似) low_mask = im <= 0.0031308 im[low_mask] *= 12.92 im[~low_mask] = 1.055 * np.power(im[~low_mask], 1/2.4) - 0.055 # 截断异常溢出值 im = np.clip(im, 0, 1) # 存8位PNG/JPG乘255转uint8,存16位PNG乘65535转uint16即可 im = (im * 255).astype(np.uint8) im = cv2.cvtColor(im, cv2.COLOR_RGB2BGR) cv2.imwrite(r"D:\CG_CONTENT\HDRIS\HDRI_BROWSER\Concrete_Office_Outside_sm_correct.png", im)
注意事项
- 不要跳过Gamma校正直接存线性值:所有普通图片查看器默认按sRGB曲线解析图片像素值,直接存线性值相当于把未编码的数据按编码后规则解析,暗部会被严重压暗,就是你截图左侧的效果。
- 不要用
im ** (1/2.2)做简化计算:sRGB的Gamma曲线是分段设计的,低亮度区为线性段,直接套2.2次幂会导致暗部偏色,用官方分段公式精度最高。 - Windows路径前加
r前缀是为了避免反斜杠被Python识别为转义符,防止读/写文件报错。 - 如果你需要保留更多高光细节,可以替换色调映射部分的算法,比如用
cv2.createTonemapDrago()、cv2.createTonemapMantiuk()等OpenCV内置的色调映射算子,调整参数到你满意的效果即可。

内容的提问来源于stack exchange,提问作者johancc
相关产品推荐
相关产品推荐

