低光场景下捕获图像的 ARKit 颜色校正
ARKit color correction of captured image in low light scenes
我有一个 ARKit 应用可以执行以下操作:
- 将帧的 captured image 渲染为纹理。
- 将此纹理应用于 AR 场景中的 scenekit 对象。
我用它来创建一个完美融入 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 纹理。我做了一些基本的伽马校正,使生成的纹理看起来正确。
我将输出存储在金属纹理 (.rgba8Unorm
) 中,然后有一个 SceneKit 对象使用该纹理作为其漫反射组件。
同样,这一切都适用于光线充足的场景。然而,在低光场景中,捕获的纹理与背景图像明显不同。
下面的示例图片显示了问题。你可以看到一条清晰的线(就在头发的边缘附近),这是人脸模型结束和背景图像开始的地方:
这在光线充足的场景中是看不到的。
问题不是错位,在黑暗场景中,虚拟面部纹理上的颜色看起来更饱和 and/or 噪点更少。
可能是什么原因造成的,我该如何避免?
您使用的是近似伽马校正,不是 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) 的最后一页。
我有一个 ARKit 应用可以执行以下操作:
- 将帧的 captured image 渲染为纹理。
- 将此纹理应用于 AR 场景中的 scenekit 对象。
我用它来创建一个完美融入 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 纹理。我做了一些基本的伽马校正,使生成的纹理看起来正确。
我将输出存储在金属纹理 (.rgba8Unorm
) 中,然后有一个 SceneKit 对象使用该纹理作为其漫反射组件。
同样,这一切都适用于光线充足的场景。然而,在低光场景中,捕获的纹理与背景图像明显不同。
下面的示例图片显示了问题。你可以看到一条清晰的线(就在头发的边缘附近),这是人脸模型结束和背景图像开始的地方:
这在光线充足的场景中是看不到的。
问题不是错位,在黑暗场景中,虚拟面部纹理上的颜色看起来更饱和 and/or 噪点更少。
可能是什么原因造成的,我该如何避免?
您使用的是近似伽马校正,不是 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) 的最后一页。