Unity中Barracuda推理ONNX模型时色彩空间不匹配导致与Python推理结果差异过大的问题求助
Unity中Barracuda推理ONNX模型时色彩空间不匹配导致与Python推理结果差异过大的问题求助
嘿,我一眼就看出你踩了Unity和Python跨平台推理时最常见的色彩空间gamma校正不匹配的坑!我之前做语义分割模型移植的时候也卡过一模一样的问题,给你把根因和解决方法讲得明明白白:
问题本质:你喂给模型的颜色空间错了!
你看Unity打印的0.196和Python输出的0.533,这俩数值正好对应线性空间转sRGB空间的关系:pow(0.196, 1/2.2) ≈ 0.533。具体为啥会这样:
- 你的RenderTexture用的是
R8G8B8A8_SRGB格式,相机渲染后会把线性空间的画面做gamma校正,转成sRGB格式存储 - 但Unity里你用
TextureFormat.RGBA32(线性格式)读取像素时,会自动把sRGB格式的存储值转成线性空间的数值,所以GetPixels()拿到的Color.r/g/b是线性空间的0-1值 - 而Python里PIL打开JPG(天生sRGB格式)后,
ToTensor()只是把0-255的像素值缩放到0-1的sRGB数值,完全没做gamma转换——你的模型是用sRGB空间的图片训练的,喂线性空间的值进去,结果能对才怪!
两种解决方法,选哪个都行
方法一:直接用sRGB格式的Texture2D读像素(最简单!)
让Unity读取像素时不做gamma转换,直接拿到和Python一致的sRGB空间数值:
修改你Start()里创建inputTexture的代码:
// 把原来的RGBA32改成RGBA32_SRGB(sRGB专用格式的Texture2D) inputTexture = new Texture2D(modelInputWidth, modelInputHeight, TextureFormat.RGBA32_SRGB, false);
还有重新加载JPG的Texture2D也要改成sRGB格式:
// 把TextureFormat.RGBA32换成RGBA32_SRGB Texture2D jpgTex = new Texture2D(2, 2, TextureFormat.RGBA32_SRGB, false, false); jpgTex.LoadImage(jpgBytes);
这样GetPixels()拿到的r/g/b就是和Python完全对齐的sRGB空间0-1值,直接喂给模型就行,不需要额外转换。
方法二:手动把线性空间值转成sRGB空间
如果不想改Texture2D的格式,就在生成Tensor的时候做gamma逆转换:
先写个转换函数:
float LinearToSRGB(float linearValue) { if (linearValue <= 0.0031308f) { return linearValue * 12.92f; } else { return 1.055f * Mathf.Pow(linearValue, 1f / 2.4f) - 0.055f; } }
然后修改Tensor转换的循环代码:
Color[] pixels = jpgTex.GetPixels(); float[] floatValues = new float[pixels.Length * 3]; for (int i = 0; i < pixels.Length; i++) { Color c = pixels[i]; // 把线性空间颜色转成sRGB空间,再赋值给Tensor floatValues[i * 3 + 0] = LinearToSRGB(c.r); floatValues[i * 3 + 1] = LinearToSRGB(c.g); floatValues[i * 3 + 2] = LinearToSRGB(c.b); }
嫌写函数麻烦?直接用Unity自带的API偷懒:
Color sRGBColor = Color.LinearToGammaSpace(c); floatValues[i * 3 + 0] = sRGBColor.r; floatValues[i * 3 + 1] = sRGBColor.g; floatValues[i * 3 + 2] = sRGBColor.b;
验证方式
改完之后再打印Tensor前12个值,应该会和Python的0.533左右的数值完全一致,这时候再跑推理,结果就会和Python里的正确输出对齐了!
内容来源于stack exchange
相关产品推荐
相关产品推荐

