【问题标题】:ARKit color correction of captured image in low light scenesARKit 在低光场景中对捕获的图像进行色彩校正
【发布时间】:2021-07-20 13:16:11
【问题描述】:

我有一个 ARKit 应用,它执行以下操作:

  • 将帧的captured image 渲染为纹理。
  • 将此纹理应用于 AR 场景中的场景套件对象。

我用它来创建一个完美融入 AR 场景的虚拟对象。

我目前的方法适用于光线充足的场景,但在黑暗场景中,虚拟对象上的纹理与当前场景背景略有不同。是什么原因造成的,有什么办法可以解决吗?

详情

我创建了 this branch on a demo project 来演示这个问题。

该项目渲染一个使用帧的 currentImage 纹理化的面部模型。结果应该是面部模型实际上变得不可见,即使它仍在渲染中。但在光线较暗的情况下,您可以清楚地看到背景图像的结束位置和人脸模型的开始位置。

这里是我用来捕捉纹理的着色器的概述

// Take in the current captured image. 
fragment float4 fragmentShader(TextureInOut in [[stage_in]],
                               texture2d<float, access::sample> capturedImageTextureY [[texture(0)]],
                               texture2d<float, access::sample> capturedImageTextureCbCr [[texture(1)]])
{
    
    // Sample from the captured image
    const float4 ycbcr = float4(capturedImageTextureY.sample(textureSampler, in.screenSpaceTexCoord).r,
                                capturedImageTextureCbCr.sample(textureSampler, in.screenSpaceTexCoord).rg, 1.0);
    
    
    // Convert to RGB
    const float3 color = (ycbcrToRGBTransform * ycbcr).rgb;
    
    // Gamma correction
    return float4(pow(color, float3(2.2)), 1);
}

此着色器采用帧的captured image 并写出RGB 纹理。我做了一些基本的 Gamma 校正,以使生成的纹理看起来正确。

我将输出存储在金属纹理 (.rgba8Unorm) 中,然后有一个 SceneKit 对象使用该纹理作为其漫反射组件。

同样,这一切都适用于光线充足的场景。然而,在低光场景中,捕捉到的纹理与背景图像明显不同。

下面的示例图像显示了问题。您可以在面部模型结束和背景图像开始的位置看到一条清晰的线(就在头发边缘附近):

这在光线充足的场景中不可见。

问题不在于未对齐,在黑暗场景中,虚拟面部纹理上的颜色看起来更饱和和/或噪点更少。

这可能是什么原因造成的,我该如何避免?

【问题讨论】:

    标签: scenekit arkit metal


    【解决方案1】:

    您使用的是近似伽马校正,它不是 RGB 和 sRGB 之间的正确转换。您真正想要做的是规避 SceneKits 默认像素格式 (sRGB)。在进行 YCbCr 到 RGB 转换之后的片段着色器中,您有线性 RGB,但是当您从片段着色器写入纹理时,来自着色器的值将被解释为 sRGB,因此会发生 RGB 到 sRGB 的转换,即(本质上pow 1/2.4,因此为什么你试图用近似的伽马校正来校正它),你需要做相反的事情来规避这个,就像你从 sRGB 到线性一样。 RGB-sRGB 转换(反之亦然)可能会令人困惑,因为有时您可能没有意识到在幕后发生的事情。所以解决方法是,而不是你的伽玛校正这样做:

    float3 color2;
    color2.x = ( color.r > 0.04045 ) ? pow((color.r + 0.055) / 1.055, 2.4) : color.r / 12.92;
    color2.y = ( color.g > 0.04045 ) ? pow((color.g + 0.055) / 1.055, 2.4) : color.g / 12.92;
    color2.z = ( color.b > 0.04045 ) ? pow((color.b + 0.055) / 1.055, 2.4) : color.b / 12.92;
    
    return float4(color2, 1.0);
    

    有关这方面的更多信息,请查看当前 Metal 着色语言指南 (v 2.3) 的最后一页。

    【讨论】:

      猜你喜欢
      • 2020-09-22
      • 2022-10-15
      • 2018-01-31
      • 1970-01-01
      • 1970-01-01
      • 2014-12-31
      • 1970-01-01
      • 2013-06-01
      • 2019-06-19
      相关资源
      最近更新 更多