将YUV420(NV12)上采样到YUV422的正确方法是什么?

时间:2017-04-24 16:29:26

标签: image-processing yuv

我有一张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 

现在我只是重复紫外线样本,但这不是正确的方法。所以,问题是,是否有一种标准的方法进行色度上采样?有人可以指导我理论吗?

注意:我想实现这一点,并且对使用它的工具不感兴趣。有兴趣,如果你可以指导我这些工具的源代码,根据一些标准(假设有一个:D)

由于

2 个答案:

答案 0 :(得分:3)

你基本上问我是否有一个N(其中N =高度/ 2)[垂直]样本(恰好是U - 或者可能是V)的数组,如何将其转换为N的数组* 2个样本具有正确的插值?答案确实是插值。由于问题的范围,我将忽略横向方面,但也应该很容易理解。

首先:色度定位。假设我有一个N * 2 Y [垂直]样本数组,U(或V)大小的数组只有N.很明显,色度子采样意味着对于每2个Y样本,只有一个U(或者V)样本[垂直]。但它并没有告诉你U / V样本的位置。在yuv422 [垂直]中,很明显,每个U(或V)的垂直位置与Y样本的垂直位置完全对齐。但对于二次采样的yuv420?第一个U值的垂直位置的中心是否与第一个Y值[“top”]的垂直位置对齐?或者恰好在第一个和第二个Y样本[“中间”]之间?或者(这可能很奇怪,但理论上可能会发生)第二个Y样本的中心[“底部”]?

Y1 U <- top    Y1                Y1
.              .  U <- center    .
Y2             Y2                Y2 U <- bottom

对于上下文,这是H.264标题中SPS的VUI中的“chroma_sample_location_type”元素。

接下来,我们如何处理这些信息?好吧,从yuv420到yuv422的插值基本上意味着[垂直]增加分辨率乘以2。现在想象一下,你有一个灰度图像,你想要提高分辨率。您使用缩放算法,缩放意味着插值。目标和源高度是彼此精确倍数的事实是一种特殊情况,但是必须使用缩放算法(即缩放滤波器)的事实不会改变。那么,你使用什么过滤器?

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(或1D:线性)。在这里,您使用两个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时,例如维基百科,您会注意到这是一个完整的研究领域,并且有更复杂的算法可用,例如bicubic(或1D:立方体)或lanczos。对于这些,IMO在这里解释它们太过分了,只需在维基百科上查找函数并根据需要进行操作。哪一个适合你是一个品味问题 - 或者更好,它主要取决于你想要如何平衡质量和速度。较高抽头滤波器(lanczos&gt; cubic&gt;线性&gt;最近邻居)将提供更好的质量,但也会在计算上更慢。

最后,你提到你自己有兴趣这样做,这就是为什么我在这里解释这一切。但请理解,编写无错误,高质量的多抽头过滤功能(例如对于lanczos,甚至是双三次)实际上需要花费相当多的时间/精力,并且需要有关矢量处理的重要知识(SIMD,例如x86 AVX / SSE或手臂霓虹灯)实际上是有用的。如果你的最终目标是在任何严肃的环境中使用它,你可能想要使用实现这些算法的现有软件,例如ffmpeg中的swscale,只是因为他们已经实现了所有这些。

答案 1 :(得分:0)

只需加倍UV平面的每个字节。或者你可以在连续的字节之间取平均值。

您也可以尝试在libswscale(来自ffmpeg)中使用调试器来查看它们的功能。如果您很难或者您不知道如何做到这一点,您可以拍摄一些图片并将其转换为YUV 420,然后将YUV420转换为YUV422,然后从源U帧打印几个字节来自结果U帧的字节和字节,看看做了什么样的数学运算。最有可能只是加倍你会得到视觉上可接受的结果。