如何在自定义 Metal Core Image Kernel 中正确处理 HDR10?
How to correctly handle HDR10 in custom Metal Core Image Kernel?
我有一个自定义的 Metal Core Image 内核(用 CIImageProcessorKernel
编写),我正在尝试使其与 HDR 视频(开始 HDR10 PQ)一起正常工作。
我知道对于 HDR 视频,进入着色器的 rgb 值可以低于 0.0 或高于 1.0。但是,我不明白视频中的10位整数值(即0-1023)是如何映射到浮点数的。
浮点数的最小值和最大值是多少? IE。 1023(纯白色)像素在着色器中的浮点数是多少。
在 WWDC20 11:32 session 10009, 使用 AVFoundation 编辑和播放 HDR 视频时,有一个不支持 HDR 的 Core Image Metal 内核的示例,因此无法正常工作。它通过从 1.0 中减去它们来反转输入的值,当 1.0 不是最大可能值时,这显然会崩溃。应该如何实现 HDR 感知?
extern “C” float4 ColorInverter(coreimage::sample_t s, coreimage::destination dest) {
return float4(1.0 - s.r, 1.0 - s.g, 1.0 - s.b, 1.0);
}
在您的内核中,颜色通常根据基础颜色 space 归一化为 [0.0 ... 1.0]
、。因此,即使值以 10 位的间隔存储在纹理中,您的着色器也会将它们作为规范化浮点数获取。
我在上面强调了颜色 space,因为它用于将源中的颜色转换为那些标准化值。当您使用默认的 sRGB 颜色 space 时,HDR 源的宽色域不适合 sRGB [0.0 ... 1.0]
光谱。这就是为什么您可能会在内核中获得超出该范围的值。这在大多数情况下实际上很有用,因为大多数为 sRGB 设计的滤镜操作在那时仍然有效。然而,上面的颜色反转示例不是。
我知道这里有两个选项:
您可以将正在使用的 CIContext
的 workingColorSpace
更改为输入的 HDR 颜色 space:
let ciContext = CIContext(options: [.workingColorSpace: CGColorSpace(name: CGColorSpace.itur_2020)!])
那么在你的内核中所有颜色值都应该被限制为 [0.0 ... 1.0]
,其中 0.0
是最暗的 HDR 颜色值,1.0
是最亮的。然后,您可以使用 1.0 - x
安全地执行反演。但是,请记住,其他一些过滤器将不会产生正确的结果,因为它们假设输入是(线性)sRGB——Core Image 的默认值。
第二个选项是您 convert(“颜色匹配”)输入到正确的颜色 space before将其传递到您的内核并在返回之前再次返回工作 space:
let colorSpace = CGColorSpace(name: CGColorSpace.itur_2020)!
let colorMatchedInput = inputImage.matchedFromWorkingSpace(to: colorSpace)
let kernelOutput = myKernel.apply(..., [colorMatchedInput, ...])
return kernelOutput.matchedToWorkingSpace(from: colorSpace)
我有一个自定义的 Metal Core Image 内核(用 CIImageProcessorKernel
编写),我正在尝试使其与 HDR 视频(开始 HDR10 PQ)一起正常工作。
我知道对于 HDR 视频,进入着色器的 rgb 值可以低于 0.0 或高于 1.0。但是,我不明白视频中的10位整数值(即0-1023)是如何映射到浮点数的。
浮点数的最小值和最大值是多少? IE。 1023(纯白色)像素在着色器中的浮点数是多少。
在 WWDC20 11:32 session 10009, 使用 AVFoundation 编辑和播放 HDR 视频时,有一个不支持 HDR 的 Core Image Metal 内核的示例,因此无法正常工作。它通过从 1.0 中减去它们来反转输入的值,当 1.0 不是最大可能值时,这显然会崩溃。应该如何实现 HDR 感知?
extern “C” float4 ColorInverter(coreimage::sample_t s, coreimage::destination dest) {
return float4(1.0 - s.r, 1.0 - s.g, 1.0 - s.b, 1.0);
}
在您的内核中,颜色通常根据基础颜色 space 归一化为 [0.0 ... 1.0]
、。因此,即使值以 10 位的间隔存储在纹理中,您的着色器也会将它们作为规范化浮点数获取。
我在上面强调了颜色 space,因为它用于将源中的颜色转换为那些标准化值。当您使用默认的 sRGB 颜色 space 时,HDR 源的宽色域不适合 sRGB [0.0 ... 1.0]
光谱。这就是为什么您可能会在内核中获得超出该范围的值。这在大多数情况下实际上很有用,因为大多数为 sRGB 设计的滤镜操作在那时仍然有效。然而,上面的颜色反转示例不是。
我知道这里有两个选项:
您可以将正在使用的 CIContext
的 workingColorSpace
更改为输入的 HDR 颜色 space:
let ciContext = CIContext(options: [.workingColorSpace: CGColorSpace(name: CGColorSpace.itur_2020)!])
那么在你的内核中所有颜色值都应该被限制为 [0.0 ... 1.0]
,其中 0.0
是最暗的 HDR 颜色值,1.0
是最亮的。然后,您可以使用 1.0 - x
安全地执行反演。但是,请记住,其他一些过滤器将不会产生正确的结果,因为它们假设输入是(线性)sRGB——Core Image 的默认值。
第二个选项是您 convert(“颜色匹配”)输入到正确的颜色 space before将其传递到您的内核并在返回之前再次返回工作 space:
let colorSpace = CGColorSpace(name: CGColorSpace.itur_2020)!
let colorMatchedInput = inputImage.matchedFromWorkingSpace(to: colorSpace)
let kernelOutput = myKernel.apply(..., [colorMatchedInput, ...])
return kernelOutput.matchedToWorkingSpace(from: colorSpace)