在C++开发中处理HDR高动态范围图像时,OpenEXR是最常用的格式之一,它支持16位半精度浮点像素存储,能够完整保留场景的高光和暗部细节。OpenEXR官方提供了C++开发库,开发者可以直接调用其接口完成图像的读写操作。

OpenEXR库基础概念
OpenEXR的核心像素类型分为多种,处理HDR图像最常用的是RGBA格式,每个通道采用half类型存储,也就是16位半精度浮点数。半精度浮点数的取值范围和普通float一致,但是存储占用只有2字节,适合大量像素数据的存储。
库的核心读写类主要包括:
Imf::RgbaInputFile:用于读取EXR格式文件Imf::RgbaOutputFile:用于写入EXR格式文件Imf::Rgba:表示单个RGBA像素的结构体,包含r、g、b、a四个half类型成员
环境配置与依赖引入
在使用OpenEXR库之前需要先完成环境配置,以Linux系统为例,可以通过包管理器安装开发依赖:
# Ubuntu/Debian系统安装命令 sudo apt-get install libopenexr-dev
如果是Windows系统,可以从OpenEXR官方仓库下载编译好的库文件,或者使用vcpkg包管理器安装:
vcpkg install openexr
在C++项目中引入头文件即可使用相关接口:
#include <ImfRgbaFile.h> #include <ImfArray.h> #include <half.h> #include <iostream>
读取HDR EXR图像
读取EXR图像的核心步骤是创建输入文件对象,获取图像尺寸,分配像素存储数组,最后读取像素数据。下面是完整的读取示例代码:
#include <ImfRgbaFile.h>
#include <ImfArray.h>
#include <half.h>
#include <iostream>
int read_exr_image(const char* file_path) {
try {
// 创建RGBA输入文件对象
Imf::RgbaInputFile in_file(file_path);
// 获取图像宽度和高度
Imath::Box2i data_window = in_file.dataWindow();
int width = data_window.max.x - data_window.min.x + 1;
int height = data_window.max.y - data_window.min.y + 1;
std::cout << "图像尺寸: " << width << "x" << height << std::endl;
// 分配像素存储数组,数组大小为width*height
Imf::Array2D<Imf::Rgba> pixels;
pixels.resizeErase(height, width);
// 读取像素数据到数组
in_file.setFrameBuffer(&pixels[0][0] - data_window.min.x - data_window.min.y * width, 1, width);
in_file.readPixels(data_window.min.y, data_window.max.y);
// 打印第一个像素的RGB值,验证读取结果
Imf::Rgba first_pixel = pixels[0][0];
std::cout << "第一个像素RGB值: "
<< first_pixel.r << ", "
<< first_pixel.g << ", "
<< first_pixel.b << std::endl;
return 0;
} catch (const std::exception& e) {
std::cerr << "读取EXR图像失败: " << e.what() << std::endl;
return -1;
}
}
写入HDR EXR图像
写入EXR图像的流程和读取相反,需要先准备像素数据,创建输出文件对象,设置图像尺寸,最后将像素数据写入文件。下面是生成渐变HDR图像并写入文件的示例代码:
#include <ImfRgbaFile.h>
#include <ImfArray.h>
#include <half.h>
#include <iostream>
int write_exr_image(const char* file_path, int width, int height) {
try {
// 分配像素数组,生成渐变HDR图像
Imf::Array2D<Imf::Rgba> pixels;
pixels.resizeErase(height, width);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// 生成从暗到亮的渐变,值范围0到10,属于HDR高动态范围
float intensity = (float)x / width * 10.0f;
pixels[y][x].r = half(intensity);
pixels[y][x].g = half(intensity * 0.8f);
pixels[y][x].b = half(intensity * 0.5f);
pixels[y][x].a = half(1.0f); // alpha通道设为不透明
}
}
// 创建输出文件,设置数据窗口和显示窗口
Imf::RgbaOutputFile out_file(
file_path,
width,
height,
Imf::WRITE_RGBA
);
// 设置帧缓冲区并写入像素
out_file.setFrameBuffer(&pixels[0][0], 1, width);
out_file.writePixels(height);
std::cout << "EXR图像写入成功: " << file_path << std::endl;
return 0;
} catch (const std::exception& e) {
std::cerr << "写入EXR图像失败: " << e.what() << std::endl;
return -1;
}
}
常见问题与注意事项
在使用OpenEXR库时需要注意几个常见问题:
half类型和float类型可以直接互相转换,但是运算时建议先转成float,避免精度损失- 读取图像时需要先获取
dataWindow的尺寸,不要假设图像从(0,0)开始存储 - 如果图像包含多个通道或者不是RGBA格式,需要使用更底层的
Imf::InputFile和Imf::OutputFile接口,手动处理通道数据 - 处理大尺寸HDR图像时注意内存占用,
Imf::Array2D会直接分配连续内存,尺寸过大会导致内存溢出
完整调用示例
下面是一个完整的main函数示例,先写入一张HDR图像,再读取验证:
int main() {
// 写入一张512x512的HDR EXR图像
int ret = write_exr_image("test_hdr.exr", 512, 512);
if (ret != 0) {
return ret;
}
// 读取刚写入的图像
ret = read_exr_image("test_hdr.exr");
return ret;
}
OpenEXRHDRIhalf_floatC++图像处理ImfRgbaFile修改时间:2026-07-01 01:24:43