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

从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 06:55:17