Search code examples
pythonyuv

YUV subsampling chroma channels in python


When halving chroma channels width and/or height, what's the correct way to subsamble? To take one chroma pixel for each 2x2 luma pixel, when sampling from a full-resolution chroma source, which chroma pixel do we take - top left? Average of all 4? Doesn't matter?

This is my current code

if field == 'top':
    i = 0
elif field == 'bottom':
    i = 1

U = fromstring(Udata, dtype='uint8', count=w/2*h).reshape(h,w/2)

# halve chroma height (it's already half-width from UYVY source) by line skipping
U = U[i::2]

# scale chroma by a factor of 0.5 (2x2 pixels in -> 1 pixel out)
U = (U[0::2, 0::2]>>2) + (U[0::2, 1::2]>>2) + (U[1::2, 0::2]>>2) + (U[1::2, 1::2]>>2) + \
  (((U[0::2, 0::2]%4)  + (U[0::2, 1::2]%4)  + (U[1::2, 0::2]%4)  + (U[1::2, 1::2]%4)) >> 2)

Solution

  • Ideally you would interpolate the data to produce the best possible image, however I think that your question is best answered with a quick overview of WHERE the luma and chroma samples are located (imagine a camera with one grid for luma and the other for chroma). For subsampling there are multiple standards for selecting the location of chroma relative to luma samples.

    most often the chroma is either co-located with the "top left" luma pixel,

    XO  X   XO  X
    
    X   X   X   X
     
    XO  X   XO  X
     
    X   X   X   X
    

    or is located in the center of the square,

    X   X   X   X
      O      O
    X   X   X   X
    
    X   X   X   X
      O      O
    X   X   X   X
    

    or is located on the left side of the square between the two left luma samples.

    X   X   X   X
    O       O
    X   X   X   X
    
    X   X   X   X
    O       O
    X   X   X   X
    

    notice that in all cases the chroma value co-located with at least some luma samples will be a combination of several chroma values, this is generally done with a FIR filter, which is why another filter is recommended for the reverse operation (however you can get OK results with averaging)