将 YUV420 (NV12) 上采样到 YUV422 的正确方法是什么?
What is the right way of upsampling YUV420 (NV12) to YUV422?
我有一个 YUV420 图像(NV12 图像,但应该无关紧要)。我正在尝试将其上采样到 YUV422。
问题是我无法找到需要给予 YUV420 中的 UV 样本的正确权重来计算 YUV422 图像中的 UV。
x -> Y
0 -> UV
YUV420 YUV422
x x x x x x x x
o o o o
x x x x x x x x
to o o
x x x x x x x x
o o o o
x x x x x x x x
o o
现在我只是在重复 UV 样本,但这不是正确的方法。所以,问题是,是否有一种标准的方法来进行色度上采样?有人可以指导我了解它的理论吗?
注意:我想实现它,但对可以实现它的工具不感兴趣。如果您能指导我查看这些根据某种标准执行此操作的工具的源代码(假设有一个 :D),我很感兴趣
谢谢
只需将 U
和 V
平面的每个字节加倍。或者您可以取连续字节之间的平均值。
您不妨尝试使用 libswscale 中的调试器(来自 ffmpeg)来逐步查看它们的作用。如果这对你来说很困难或者你不知道该怎么做,你可以拍摄一些照片并将其转换为 YUV 420,然后将 YUV420 转换为 YUV422,然后从源 U 帧和结果 U 帧打印几个字节看看做了什么样的数学运算。很可能简单地加倍你会得到视觉上可接受的结果。
你基本上是在问,如果我有一个 N(其中 N=height/2)[垂直] 样本数组(恰好是 U - 或者可能是 V),我如何将其转换为具有正确插值的 N*2 个样本数组?答案确实是插值。由于你问题的范围,我将忽略水平方面,但它也应该很容易理解。
首先:色度定位。假设我有一组 N*2 Y [垂直] 样本,并且 U(或 V)大小的数组只有 N。很明显,色度子采样意味着对于每 2 个 Y 样本,只有一个 U(或V) 样本[垂直]。但它不会告诉您 U/V 样本所在的位置。在 yuv422 [vertical] 中,这是显而易见的,每个 U(或 V)的垂直位置与 Y 样本的垂直位置完美对齐。但是对于二次采样的 yuv420?第一个U值的垂直位置的中心是否与第一个Y值的垂直位置["top"]对齐?或者恰好在第一个和第二个 Y 样本 ["middle"] 之间?或者(这很奇怪,但理论上它可能发生)第二个 Y 样本的中心 ["bottom"]?
Y1 U <- top Y1 Y1
. . U <- center .
Y2 Y2 Y2 U <- bottom
对于上下文,这是 H.264 中 SPS 的 VUI 中的 "chroma_sample_location_type" 元素 header。
接下来,我们如何处理这些信息?好吧,从 yuv420 插值到 yuv422 基本上意味着 [垂直] 将分辨率增加两倍。想象一下,现在您有一张灰度图像并且想要提高分辨率。您使用缩放算法,缩放意味着插值。目标高度和源高度是彼此的精确倍数这一事实是一种特殊情况,但您必须使用缩放算法(即缩放过滤器)的事实并没有改变。那么,您使用什么过滤器?
Nearest neighbour是最简单的,这意味着你从最近的源位置选择值:
Y1 U1in <- top Y1 U1out=U1in
. .
Y2 Y2 U2out=U1in?
. becomes .
Y3 U2in Y3 U3out=U2in
. .
Y4 Y4 U4out=U2in?
从数学上讲,U2out 也可以是 U2in,因为距离相等。在这里,也很明显为什么色度定位很重要,将其与中心进行比较:
Y1 Y1 U1out=U1in
. U1in <- center .
Y2 Y2 U2out=U1in
. becomes .
Y3 Y3 U3out=U2in
. U2in .
Y4 Y4 U4out=U2in
注意问号是如何消失的。现在,实际上还没有进行任何过滤,让我们开始吧。
最简单的过滤器是 bilinear(或一维:线性)。在这里,您使用两个 U 样本并将它们插值为一个,其中每个源像素的权重由它们与目标像素的相对距离决定。
Y1 U1in <- top Y1 U1out=U1in
. .
Y2 Y2 U2out=(U1in+U2in)/2
. becomes .
Y3 U2in Y3 U3out=U2in
. .
Y4 Y4 U4out=(U2in+U3in)/2
或:
Y1 Y1 U1out=U1in
. U1in <- center .
Y2 Y2 U2out=(U1in*3+U2in)/4
. becomes .
Y3 Y3 U3out=(U1in+U2in*3)/4
. U2in .
Y4 Y4 U4out=(U2in*3+U3in)/4
随着您搜索更多 filtering algorithms on e.g. wikipedia, you'll notice that this is a whole research area and there's more complicated algorithms available, such as bicubic(或一维:立方体)或 lanczos。对于这些,IMO 在这里解释它们太过分了,只需在维基百科上查找功能并根据需要进行操作。哪一个适合你是一个品味问题——或者更好,这基本上取决于你想如何平衡质量和速度。 Higher-tap 过滤器(lanczos > cubic > linear > nearest-neighbour)会提供更好的质量,但计算速度也会变慢。
最后,您提到您有兴趣自己做这件事,这就是我在这里解释所有这些的原因。但请理解,编写 bug-free、high-quality multi-tap 过滤函数(例如对于 lanczos,甚至双三次)实际上需要相当多的 time/effort 并且需要大量的矢量知识处理(SIMD,例如 x86 AVX/SSE 或 arm Neon)实际有用。如果您的最终目标是在任何严肃的环境中使用它,您可能 do 想要使用实现这些算法的现有软件,例如swscale 在 ffmpeg 中,仅仅是因为他们已经实现了所有这些。
我有一个 YUV420 图像(NV12 图像,但应该无关紧要)。我正在尝试将其上采样到 YUV422。
问题是我无法找到需要给予 YUV420 中的 UV 样本的正确权重来计算 YUV422 图像中的 UV。
x -> Y
0 -> UV
YUV420 YUV422
x x x x x x x x
o o o o
x x x x x x x x
to o o
x x x x x x x x
o o o o
x x x x x x x x
o o
现在我只是在重复 UV 样本,但这不是正确的方法。所以,问题是,是否有一种标准的方法来进行色度上采样?有人可以指导我了解它的理论吗?
注意:我想实现它,但对可以实现它的工具不感兴趣。如果您能指导我查看这些根据某种标准执行此操作的工具的源代码(假设有一个 :D),我很感兴趣
谢谢
只需将 U
和 V
平面的每个字节加倍。或者您可以取连续字节之间的平均值。
您不妨尝试使用 libswscale 中的调试器(来自 ffmpeg)来逐步查看它们的作用。如果这对你来说很困难或者你不知道该怎么做,你可以拍摄一些照片并将其转换为 YUV 420,然后将 YUV420 转换为 YUV422,然后从源 U 帧和结果 U 帧打印几个字节看看做了什么样的数学运算。很可能简单地加倍你会得到视觉上可接受的结果。
你基本上是在问,如果我有一个 N(其中 N=height/2)[垂直] 样本数组(恰好是 U - 或者可能是 V),我如何将其转换为具有正确插值的 N*2 个样本数组?答案确实是插值。由于你问题的范围,我将忽略水平方面,但它也应该很容易理解。
首先:色度定位。假设我有一组 N*2 Y [垂直] 样本,并且 U(或 V)大小的数组只有 N。很明显,色度子采样意味着对于每 2 个 Y 样本,只有一个 U(或V) 样本[垂直]。但它不会告诉您 U/V 样本所在的位置。在 yuv422 [vertical] 中,这是显而易见的,每个 U(或 V)的垂直位置与 Y 样本的垂直位置完美对齐。但是对于二次采样的 yuv420?第一个U值的垂直位置的中心是否与第一个Y值的垂直位置["top"]对齐?或者恰好在第一个和第二个 Y 样本 ["middle"] 之间?或者(这很奇怪,但理论上它可能发生)第二个 Y 样本的中心 ["bottom"]?
Y1 U <- top Y1 Y1
. . U <- center .
Y2 Y2 Y2 U <- bottom
对于上下文,这是 H.264 中 SPS 的 VUI 中的 "chroma_sample_location_type" 元素 header。
接下来,我们如何处理这些信息?好吧,从 yuv420 插值到 yuv422 基本上意味着 [垂直] 将分辨率增加两倍。想象一下,现在您有一张灰度图像并且想要提高分辨率。您使用缩放算法,缩放意味着插值。目标高度和源高度是彼此的精确倍数这一事实是一种特殊情况,但您必须使用缩放算法(即缩放过滤器)的事实并没有改变。那么,您使用什么过滤器?
Nearest neighbour是最简单的,这意味着你从最近的源位置选择值:
Y1 U1in <- top Y1 U1out=U1in
. .
Y2 Y2 U2out=U1in?
. becomes .
Y3 U2in Y3 U3out=U2in
. .
Y4 Y4 U4out=U2in?
从数学上讲,U2out 也可以是 U2in,因为距离相等。在这里,也很明显为什么色度定位很重要,将其与中心进行比较:
Y1 Y1 U1out=U1in
. U1in <- center .
Y2 Y2 U2out=U1in
. becomes .
Y3 Y3 U3out=U2in
. U2in .
Y4 Y4 U4out=U2in
注意问号是如何消失的。现在,实际上还没有进行任何过滤,让我们开始吧。
最简单的过滤器是 bilinear(或一维:线性)。在这里,您使用两个 U 样本并将它们插值为一个,其中每个源像素的权重由它们与目标像素的相对距离决定。
Y1 U1in <- top Y1 U1out=U1in
. .
Y2 Y2 U2out=(U1in+U2in)/2
. becomes .
Y3 U2in Y3 U3out=U2in
. .
Y4 Y4 U4out=(U2in+U3in)/2
或:
Y1 Y1 U1out=U1in
. U1in <- center .
Y2 Y2 U2out=(U1in*3+U2in)/4
. becomes .
Y3 Y3 U3out=(U1in+U2in*3)/4
. U2in .
Y4 Y4 U4out=(U2in*3+U3in)/4
随着您搜索更多 filtering algorithms on e.g. wikipedia, you'll notice that this is a whole research area and there's more complicated algorithms available, such as bicubic(或一维:立方体)或 lanczos。对于这些,IMO 在这里解释它们太过分了,只需在维基百科上查找功能并根据需要进行操作。哪一个适合你是一个品味问题——或者更好,这基本上取决于你想如何平衡质量和速度。 Higher-tap 过滤器(lanczos > cubic > linear > nearest-neighbour)会提供更好的质量,但计算速度也会变慢。
最后,您提到您有兴趣自己做这件事,这就是我在这里解释所有这些的原因。但请理解,编写 bug-free、high-quality multi-tap 过滤函数(例如对于 lanczos,甚至双三次)实际上需要相当多的 time/effort 并且需要大量的矢量知识处理(SIMD,例如 x86 AVX/SSE 或 arm Neon)实际有用。如果您的最终目标是在任何严肃的环境中使用它,您可能 do 想要使用实现这些算法的现有软件,例如swscale 在 ffmpeg 中,仅仅是因为他们已经实现了所有这些。