Resize (downsize) YUV420sp image

PerracoLabs picture PerracoLabs · Jun 19, 2013 · Viewed 9.8k times · Source

I am trying to resize (scale down) an image which comes in YUV420sp format. Is it possible to do such image resizing without converting it into RGB, so directly manipulating the YUV420sp pixel array? Where can I find such algorithm?

Thanks

Answer

Fredrik Pihl picture Fredrik Pihl · Jun 19, 2013

YUV 4:2:0 planar looks like this:

----------------------
|     Y      | Cb|Cr |
----------------------

where:

Y = width x height pixels
Cb = Y / 4 pixels
Cr = Y / 4 pixels

Total num pixels (bytes) = width * height * 3 / 2

And the subsamling used like this:

420

Which means that each chroma-pixel-value is shared between 4 luma-pixels.

One approach is just to remove pixels, making sure that corresponding Y-Cb-Cr relationship are kept/recalculated.

Something close to the Nearest-neighbor interpolation but reversed.

Another approach is to first convert the 4:2:0 subsampling to 4:4:4

444

Here you have a 1 to 1 mapping between luma and chroma data.

This is the correct way to interpolate chroma between 4:2:0 and 4:2:2 (luma is already at correct resolution) Code in python, follow html-link for c-dito. Code is not very pythonic, just a direct translation of the c-version.

def __conv420to422(self, src, dst):
    """
    420 to 422 - vertical 1:2 interpolation filter

    Bit-exact with
    http://www.mpeg.org/MPEG/video/mssg-free-mpeg-software.html
    """
    w = self.width >> 1
    h = self.height >> 1

    for i in xrange(w):
        for j in xrange(h):
            j2 = j << 1
            jm3 = 0 if (j<3) else j-3
            jm2 = 0 if (j<2) else j-2
            jm1 = 0 if (j<1) else j-1
            jp1 = j+1 if (j<h-1) else h-1
            jp2 = j+2 if (j<h-2) else h-1
            jp3 = j+3 if (j<h-3) else h-1

            pel = (3*src[i+w*jm3]
                 -16*src[i+w*jm2]
                 +67*src[i+w*jm1]
                +227*src[i+w*j]
                 -32*src[i+w*jp1]
                  +7*src[i+w*jp2]+128)>>8

            dst[i+w*j2] = pel if pel > 0 else 0
            dst[i+w*j2] = pel if pel < 255 else 255

            pel = (3*src[i+w*jp3]
                 -16*src[i+w*jp2]
                 +67*src[i+w*jp1]
                +227*src[i+w*j]
                 -32*src[i+w*jm1]
                 +7*src[i+w*jm2]+128)>>8

            dst[i+w*(j2+1)] = pel if pel > 0 else 0
            dst[i+w*(j2+1)] = pel if pel < 255 else 255
    return dst

Run this twice to get 4:4:4. Then it's just a matter of removing rows and columns.

Or you can just quadruple the chroma-pixels to go from 4:2:0 to 4:4:4, remove rows and columns and then average 4 Cb/Cr values into 1 to get back to 4:2:0 again, it all depends on how strict you need to be :-)