[Haskell-beginners] Trying to convert from planar NV12 to RGB

John Obbele john.obbele at gmail.com
Mon May 2 22:11:32 CEST 2011


I'm playing with some raw video streams. These raw data provide
YUV (or YCrCb) components interleaved between each other and it
is sometimes quite cumbersome to find a triplet of matching YUV
bytes before converting them to RGB.

For "packed" formats such as YUY2, the operation seems pretty
straight forward in Haskell:

>-- | Converting from packed YUY2 4:2:2 pixels to RGB ones.
>-- YUY2:  Y0 U0 Y1 V0 < -- 2 macro pixels --> (Y0:U0:V0)+(Y1:U0:V0)
>yuy2ToRGB :: B.ByteString -> B.ByteString
>yuy2ToRGB bs = f [] bs
>  where
>    f acc xs | B.null xs = B.pack $ reverse acc -- 
>             | otherwise = let [y0, u0, y1, v0] = B.unpack (B.take 4 xs)
>                               (r0,g0,b0) = yuv2rgb (y0,u0,v0)
>                               (r1,g1,b1) = yuv2rgb (y1,u0,v0)
>                           in f (b1:g1:r1:b0:g0:r0:acc) (B.drop 4 xs)



But on the other hand, I'm having some headaches converting from
planar NV12 to RGB.

For example, A width=4 and height=4 NV12 frame is stored in a
1-dimensional array of (4 * 4 * 3/2 = 24) elements:
| Y00 | Y01 | Y02 | Y03 | Y04 | Y05 | Y06 | Y07 | Y08 | Y09 | Y10 | Y11 | Y12 | Y13 | Y14 | Y15 | U00 | V00 | U01 | V01 | U02 | V02 | U03 | V03 |

This array could be unfold to the following matrix
representation, and you see that the luminance (Y) bytes are
provided first, followed by a sequence of (U,V) pairs.
| Y00 | Y01 | Y02 | Y03 |
| Y04 | Y05 | Y06 | Y07 |
| Y08 | Y09 | Y10 | Y11 |
| Y12 | Y13 | Y14 | Y15 |
|-----------------------|
| U00 | V00 | U01 | V01 |
| U02 | V02 | U03 | V03 |

Now, if you want to get the RGB value for every pixel, you have
to fetch the corresponding (U,V) components at the end of the
frame.

pixel00 will need (Y00,U00,V00)
pixel02 will need (Y02,U01,V01)
...
pixel15 will need (Y15,U03,V03)


So far, I have this Haskell function to convert NV12 to RGB.

>nv12ToRGB w h bs =
>    B.pack $ concatMap f [0.. w * h - 1]
>  where
>    f i = let m = (i `div` w) `div` 2  -- line index modulo 2
>              n = (i `rem` w) `div` 2  -- column index modulo 2
>              iuv = m * w `div` 2 + n
>              (r,g,b) = yuv2rgb (pixelY i, pixelU iuv, pixelV iuv)
>          in [r,g,b]
>
>    pixelY i   = B.index bs i
>    pixelU i   = B.index bs (w*h+2*i)
>    pixelV i   = B.index bs (w*h+2*i+1)


I don't know if converting ByteString to list, using map with a
not-so-simple f function, then concatenating and repacking the
ByteString is the best solution one could find in Haskell and I
would like to know if by chance some "senior" haskellers would
know some tricks (like list comprehension or
ByteString.unfoldWhatever) to improve the previous code.


regards,
/john



More information about the Beginners mailing list