<p>I convert RGB input to YUV 420 semi-planar output using compute shader.Luma plane is straightforward as it maps 1:1 with RGB dimensions. As we know, <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx" rel="nofollow noreferrer">NV12 packs Cb</a> and Cr into one plane, which means the CbCr values must be calculating from downsampled (x4) source.And this is the part I am not completely sure I am doing right. I currently try subsampling with simple bileanar filtering.And it 'almost' looks right.I uses t<a href="https://blog.demofox.org/2015/08/15/resizing-images-with-bicubic-interpolation/" rel="nofollow noreferrer">his article</a> as reference.It is possible that my code is 100% correct and the aliasing stems from compression losness. </p>

<p>Here is my part of shader code that samples from RGB texture and writes U and V values:</p>

<pre><code>ivec2 itexSize =textureSize(rbga_tex,0);
vec2 texSize =  vec2(itexSize);
vec2 texelSize = 1.0 / (texSize / 2.0);
vec2 pixel = uv * texSize + 0.5;
pixel = (floor(pixel) / texSize) - vec2(texelSize/2.0);

//bilinear filtering:
float sc = 4.0;
vec3 ts = texture(rbga_tex,pixel).rgb * 0.25; // center
vec3 tr = texture(rbga_tex,pixel + vec2(texelSize.x * sc, 0.0)).rgb  * 0.25;
vec3 bl = texture(rbga_tex,pixel + vec2(0.0, texelSize.y * sc)).rgb * 0.25;
vec3 br = texture(rbga_tex,pixel + vec2(texelSize.x,texelSize.y) * sc).rgb * 0.25;

vec3 tex1 = (ts + tr + bl + br) ;

// calculate and write U...

// calculate and write V...
</code></pre>

<p>Here is the original rgb image:</p>

<p><a href="https://i.sstatic.net/lQ7Og.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/lQ7Og.png" alt="enter image description here"></a></p>

<p>And here is the snapshot of the video frame after the conversion:</p>

<p><a href="https://i.sstatic.net/8lm8a.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/8lm8a.png" alt="enter image description here"></a></p>

<p>So here are my questions:</p>

<p>a)I would like to understand if there is something I am doing wrong, and if possible to get a concise explanation on how subsampling should be done when converting from rbg to yuv420 color space. </p>

<p>b)If this result is correct, are there ways to get smoother effect while keeping overall sharpness?I understand that using bicubic filter we can get smoother visuals but at cost of image sharpness.</p>

CC BY-SA 3.0
2017-12-19T04:24:26.217Z
As a minor optimisation, you should be able to compute the average of 4 texels using a single texture2D operation. If you sample from a half-pixel offset the sampling area will cover 4 texels equally.
PaulHK
CC BY-SA 3.0
2017-12-19T05:06:15.573Z
Your results look exactly like what I'd expect, and what I see from the output of popular video editing applications by the major manufacturers. Due to the low resolution of the Cb and Cr channels, I don't think you're likely to get it any sharper. That's just how 420 subsampling is. It takes up less space because it throws out data, and it isn't careful about it *at all*!
user1118321
CC BY-SA 3.0
2017-12-19T22:33:12.147Z
@PaulHK, do you mean the case where the texture has linear filtering performed by the hardware?
Michael IV
CC BY-SA 3.0
2017-12-20T04:09:01.543Z
Yes, it should be the equivalent to the 4 texture reads in the code above.
PaulHK
CC BY-SA 3.0
2017-12-20T04:10:32.813Z
To answer OP's original question: I've seen implementations of this which use seperate CrCb and Y textures (called semiplanar on some platforms). By doing this you not only simplify texel coordinate computation but you also get independent filtering on both the Chroma and Luma channels.
PaulHK
CC BY-SA 3.0
2017-12-20T08:02:43.660Z
I have also seen that. Done via fragments shader. This approach is suboptimal in terms of performance, due to higher bandwidth consumption.
Michael IV
<texture><gpu><sampling><filtering><video>
YUV (NV12) Chroma subsampling while converting from RGB image
Michael IV
# YUV (NV12) Chroma subsampling while converting from RGB image

(Asked by: username_0 on 2017-12-18)

I convert RGB input to YUV 420 semi-planar output using compute shader.Luma plane is straightforward as it maps 1:1 with RGB dimensions. As we know, [NV12 packs Cb](https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx) and Cr into one plane, which means the CbCr values must be calculating from downsampled (x4) source.And this is the part I am not completely sure I am doing right. I currently try subsampling with simple bileanar filtering.And it 'almost' looks right.I uses t[his article](https://blog.demofox.org/2015/08/15/resizing-images-with-bicubic-interpolation/) as reference.It is possible that my code is 100% correct and the aliasing stems from compression losness.

Here is my part of shader code that samples from RGB texture and writes U and V values:

```
ivec2 itexSize =textureSize(rbga_tex,0);
vec2 texSize =  vec2(itexSize);
vec2 texelSize = 1.0 / (texSize / 2.0);
vec2 pixel = uv * texSize + 0.5;
pixel = (floor(pixel) / texSize) - vec2(texelSize/2.0);

//bilinear filtering:
float sc = 4.0;
vec3 ts = texture(rbga_tex,pixel).rgb * 0.25; // center
vec3 tr = texture(rbga_tex,pixel + vec2(texelSize.x * sc, 0.0)).rgb  * 0.25;
vec3 bl = texture(rbga_tex,pixel + vec2(0.0, texelSize.y * sc)).rgb * 0.25;
vec3 br = texture(rbga_tex,pixel + vec2(texelSize.x,texelSize.y) * sc).rgb * 0.25;

vec3 tex1 = (ts + tr + bl + br) ;

// calculate and write U...

// calculate and write V...

```

Here is the original rgb image:

[![enter image description here](https://i.sstatic.net/lQ7Og.png)](https://i.sstatic.net/lQ7Og.png)

And here is the snapshot of the video frame after the conversion:

[![enter image description here](https://i.sstatic.net/8lm8a.png)](https://i.sstatic.net/8lm8a.png)

So here are my questions:

a)I would like to understand if there is something I am doing wrong, and if possible to get a concise explanation on how subsampling should be done when converting from rbg to yuv420 color space.

b)If this result is correct, are there ways to get smoother effect while keeping overall sharpness?I understand that using bicubic filter we can get smoother visuals but at cost of image sharpness.

**Tags:** texture, gpu, sampling, filtering, video

> **Comments:**
> **username_1**: As a minor optimisation, you should be able to compute the average of 4 texels using a single texture2D operation. If you sample from a half-pixel offset the sampling area will cover 4 texels equally.  
> **username_2**: Your results look exactly like what I'd expect, and what I see from the output of popular video editing applications by the major manufacturers. Due to the low resolution of the Cb and Cr channels, I don't think you're likely to get it any sharper. That's just how 420 subsampling is. It takes up less space because it throws out data, and it isn't careful about it *at all*!  
> **username_0**: @username_1, do you mean the case where the texture has linear filtering performed by the hardware?  
> **username_1**: Yes, it should be the equivalent to the 4 texture reads in the code above.  
> **username_1**: To answer OP's original question: I've seen implementations of this which use seperate CrCb and Y textures (called semiplanar on some platforms). By doing this you not only simplify texel coordinate computation but you also get independent filtering on both the Chroma and Luma channels.  
> **username_0**: I have also seen that. Done via fragments shader. This approach is suboptimal in terms of performance, due to higher bandwidth consumption.  

---

## 0 answers

