为什么归一化表面法线EXR渲染图与PNG渲染图存在差异?
读取Cycles渲染的法线EXR文件并转换为NumPy数组
我来帮你把这段读取EXR法线的代码补全并梳理清楚,毕竟用Cycles渲染的表面法线EXR文件,通道值范围是**[-1, 1]**,没法直接存储为PNG格式(PNG只支持非负的像素值范围),先把它转换成NumPy数组是后续处理的关键一步。
完整的读取与转换代码
import OpenEXR import array import Imath import numpy as np # 打开目标EXR法线文件 exr_file = OpenEXR.InputFile('normal.exr') # 定义EXR文件的像素类型为浮点型 float_pixel_type = Imath.PixelType(Imath.PixelType.FLOAT) # 获取图像的宽高尺寸(从EXR的头信息中提取数据窗口) data_window = exr_file.header()['dataWindow'] width = data_window.max.x - data_window.min.x + 1 height = data_window.max.y - data_window.min.y + 1 # 读取R、G、B三个通道的浮点数据,转换为Python列表 rgb_channel_data = [ array.array('f', exr_file.channel(channel, float_pixel_type)).tolist() for channel in ("R", "G", "B") ] # 将一维的通道数据转换为(height, width, 3)的标准图像NumPy数组 normal_np_array = np.array(rgb_channel_data).reshape(3, height, width).transpose(1, 2, 0)
代码说明
- 首先导入了所有需要的库:
OpenEXR负责读取EXR文件,array处理原始的浮点数据,Imath处理EXR的像素类型和数据窗口信息,numpy用于高效的数组操作。 - 读取EXR文件后,必须先获取图像的宽高——EXR的
dataWindow存储了图像的有效像素区域,通过计算窗口的最大/最小值差得到实际的宽高数值。 - 每个通道的原始数据是一维的浮点列表,所以我们需要用
reshape和transpose把它转换成(高度, 宽度, 3)的标准图像数组形状,这样后续的可视化或格式转换会更方便。
额外补充:将法线数组转换为可保存的PNG格式
如果需要把处理后的法线保存为PNG,我们需要把**[-1, 1]的范围线性映射到PNG支持的[0, 1]或[0, 255]**范围:
# 将[-1, 1]映射到[0, 1]的浮点范围 normal_for_png = (normal_np_array + 1) / 2.0 # 转换为8位无符号整数(0-255),适合保存为常规PNG normal_8bit = (normal_for_png * 255).astype(np.uint8) # 用PIL库保存为PNG文件 from PIL import Image Image.fromarray(normal_8bit).save('normal_converted.png')
内容的提问来源于stack exchange,提问作者Amir
相关产品推荐
相关产品推荐

