从EXR文件创建IWICBitmap时图像显示异常问题排查
我尝试从EXR文件创建IWICBitmap(已移除错误检查),相关代码如下:
#pragma pack(push,1) struct fl { float r, g, b, a; }; #pragma pack(pop) HRESULT Open(const char* f,IWICBitmap** d) { exr_context_initializer_t ctxtinit = EXR_DEFAULT_CONTEXT_INITIALIZER; exr_context_t myfile = {}; exr_result_t rv = exr_start_read(&myfile, f, &ctxtinit); int part_index = 0; const exr_attr_chlist_t* chl = 0; exr_get_channels(myfile, part_index, &chl); int32_t ck = 0; rv = exr_get_chunk_count(myfile, part_index, &ck); int32_t sl = 0; rv = exr_get_scanlines_per_chunk(myfile, part_index, &sl); int y = 0; int wi = 0; int he = 0; std::vector<fl> data; // put here the floats exr_decode_pipeline_t dec = {}; for (int32_t cuk = 0; cuk < ck; cuk++) { exr_chunk_info_t ch = {}; exr_read_scanline_chunk_info(myfile, part_index, y, &ch); wi = ch.width; he += ch.height; y += sl; bool first = 0; if (dec.decompress_fn == 0) { rv = exr_decoding_initialize(myfile, part_index, &ch, &dec); rv = exr_decoding_choose_default_routines(myfile, part_index, &dec); first = 1; } if (!first) rv = exr_decoding_update(myfile, part_index,&ch,&dec); rv = exr_decoding_run(myfile, part_index, &dec); int NumPixels = (wi * ch.height); auto BytesPerPixel = ch.unpacked_size / NumPixels; if (true) { // RGB(A) if (chl->entries[0].pixel_type == EXR_PIXEL_HALF) { if (BytesPerPixel == chl->num_channels * 2) { auto ds = data.size(); data.resize(ds + NumPixels); auto p = data.data() + ds; char* x = (char*)dec.unpacked_buffer; for (int j = 0; j < NumPixels; j++) { uint16_t* u = (uint16_t*)x; p->a = 1.0f; for (int jH = 0; jH < chl->num_channels; jH++) { half ha(Imath_3_2::half::FromBits,*u); ha.setBits(*u); if (strcmp(chl->entries[jH].name.str, "R") == 0) p->r = ha.operator float(); if (strcmp(chl->entries[jH].name.str, "G") == 0) p->g = ha.operator float(); if (strcmp(chl->entries[jH].name.str, "B") == 0) p->b = ha.operator float(); if (strcmp(chl->entries[jH].name.str, "A") == 0) p->a = ha.operator float(); u++; } x += BytesPerPixel; p++; } } else break; } if (chl->entries[0].pixel_type == EXR_PIXEL_FLOAT) { // code removed for simplicity, I guess the same issue happens here unless it's a problem of the half-float } } } rv = exr_decoding_destroy(myfile, &dec); exr_finish(&myfile); CComPtr<IWICImagingFactory2> wbfact = 0; CoCreateInstance(CLSID_WICImagingFactory2, 0, CLSCTX_INPROC_SERVER, __uuidof(IWICImagingFactory2), (void**)&wbfact); return wbfact->CreateBitmapFromMemory(wi, he, GUID_WICPixelFormat128bppPRGBAFloat, wi * 16,(UINT)data.size()*16, (BYTE*)data.data(), d); }
读取的像素数量正确(示例图像为800x800),但生成的图像与Photoshop打开的结果存在差异,我使用OpenEXR的Imath库处理半浮点数,请问代码中存在什么问题?
以下是代码中导致图像显示异常的关键问题:
半浮点数转换重复操作:
你在初始化half对象时,同时调用了带FromBits的构造函数和setBits(*u),这属于重复赋值操作,会干扰正确的半浮点数转换逻辑。正确写法只需二选一:// 方式1:直接通过FromBits构造 half ha = Imath_3_2::half::FromBits(*u); // 方式2:先构造再设置位 half ha; ha.setBits(*u);扫描线顺序不匹配:
OpenEXR默认采用从上到下的扫描线存储顺序,但WIC的CreateBitmapFromMemory要求像素数据是从下到上的顺序(即图像第一行对应数据的最后一行)。当前直接按EXR顺序存储,会导致图像上下颠倒,这是和Photoshop显示差异的核心原因之一。
修复方式:可以在收集完所有像素后,反转整个data的行顺序;或者在读取每个chunk时,将chunk内的扫描线逆序存入data。内存对齐冗余设置:
你给fl结构体添加了#pragma pack(push,1)强制1字节对齐,但GUID_WICPixelFormat128bppPRGBAFloat格式要求每个像素16字节(4个float自然对齐),强制1字节对齐会降低内存访问效率,甚至在部分平台引发未定义行为,建议移除该对齐指令。色彩空间与通道映射潜在问题:
Photoshop打开EXR时会默认应用色彩管理(比如转换为sRGB),而你的代码直接输出线性颜色空间的像素数据,这会导致色彩显示差异。另外需确认EXR的通道顺序是否完全匹配WIC的PRGBA格式,避免通道映射错误。
内容的提问来源于stack exchange,提问作者Michael Chourdakis

