You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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内置的色调映射算子,调整参数到你满意的效果即可。

output vs desired output

内容的提问来源于stack exchange,提问作者johancc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 06:48:24