[XviD-devel] RGB to YV12

Edouard Gomez xvid-devel@xvid.org
Thu, 16 Jan 2003 21:39:36 +0100


--dTy3Mrz/UPE2dbVg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

anthony belista (anthonyb@stargames.com.au) wrote:
> Hello Pete,
>=20
> I understand you're working on some yv12 decoding. I wonder if you can gi=
ve me=20
> some info about encoding the rgb back to yv12. I can't figure out how it =
will=20
> be mapped in the memory. thanks alot. sorry if I cause you any trouble.

I'll do a quick answer because this is covered in lot of sites out there
on the web (google is your friend)

RGB is mostly used in its interleaved format which is mapped like this
in memory (i'm talking about RGB24 which is 1byte per color component):

For each row you have:

                       width*3-2
                       | width*3-1
byte  1 2 3 4 5 6  ... | | width*3
      | | | | | |      | | |
      R.G.B R.G.B  ... R.G.B

An Y row begins  at byte Y*width*3. Then a pixel on  horizontal pos X on
that row can be accessed using an offset of X*3.

This gives a simple formula:
uchar *pixel_addr =3D image_ptr + (X+width*Y)*3;=20

Then you can get the color components easily doing this:
uchar R =3D *(pixel_addr + 0);
uchar G =3D *(pixel_addr + 1);
uchar G =3D *(pixel_addr + 2);

Now truning an RGB image to an YV12 image is not as easy as picking a
single RGB pixel.

The first difference between RGB and YV12 is that YV12 uses a planar
representation of the components that means that you'll have the Y plane
then the U plane and finally the V plane. RGB used an interleaved format
as you found each components "mixed together" RGB then RGB again etc etc

Anyway, the relation to obtain YUV data from RGB pixels is linear
operation using a 3x3 matrix (i don't have the coeeficients, but this
does not hurt for the explanation)

[Y]   [c00 c01 c02][R]
[U] =3D [c10 c11 c12][G]
[V]   [c20 c21 c22][B]

This gives:
Y =3D c00*R + c01*G + c02*B;
U =3D c10*R + c11*G + c12*B;
V =3D c20*R + c21*G + c22*B;

Another special thing to take care is that YV12 format downsamples U and
V planes by a factor 2  on both horizontal and vertical directions. This
means that 4 RGB pixels will generate 4  Y samples, but only 1 U and 1 V
sample.=20

basically for a four block RGB pixel this gives:

RGB RGB                  Y  Y
           will become    UV
RGB RGB                  Y  Y
24bpp (3 bytes perpixel) (4*8+2*8)/4bpp =3D=3D 12bpp (3/2 bytes per pixel)

Now the overall algorithm is:

uchar rgb_image[width*height*3]
uchar yuv_image[width*height*3/2]
uchar *y =3D yuv_image;          /* The Y plane is the first one */
uchar *u =3D y + width*height;   /* Then the U plane after a widthxheight Y=
 plane */
uchar *v =3D u + width*height/4; /* Then the V plane after a (width/2)*(hei=
ght/2)=3D=3Dwidth*height/4 U plane */

/* Macros to make the code readable */
#define R(x,y) rgb_image[((y)*width(x))*3 + 0]
#define G(x,y) rgb_image[((y)*width(x))*3 + 1]
#define B(x,y) rgb_image[((y)*width(x))*3 + 2]

for(int y=3D0; y<height/2; y++) {
  for(int x=3D0; x<width/2; x++) {

    int _u, _v;

    /* Compute a 2x2 Y block from current 2x2 RGB block */
    *(y +   0   + 0) =3D c00*R(x  ,y  ) + c01*G(x  ,y  ) + c02*B(x  ,y  ); =
/* pixel at x,y */
    *(y +   0   + 1) =3D c00*R(x+1,y  ) + c01*G(x+1,y  ) + c02*B(x+1,y  ); =
/* pixel at its right */
    *(y + width + 0) =3D c00*R(x  ,y+1) + c01*G(x  ,y+1) + c02*B(x  ,y+1); =
/* pixel below the first one */
    *(y + width + 1) =3D c00*R(x+1,y+1) + c01*G(x+1,y+1) + c02*B(x+1,y+1); =
/* pixel below the second one */

    /* We first compute four U samples corresponding to our four
     * RGB pixels */=20
    _u  =3D c10*R(x  ,y  ) + c11*G(x  ,y  ) + c12*B(x  ,y  );
    _u +=3D c10*R(x+1,y  ) + c11*G(x+1,y  ) + c12*B(x+1,y  );
    _u +=3D c10*R(x  ,y+1) + c11*G(x  ,y+1) + c12*B(x  ,y+1);
    _u +=3D c10*R(x+1,y+1) + c11*G(x+1,y+1) + c12*B(x+1,y+1);

    /* Then we average these four U samples that have been accumulated */
    *u =3D _u/4;

    /* Same thing for the Y sample */

    /* We first compute four U samples corresponding to our four
     * RGB pixels */=20
    _v  =3D c20*R(x  ,y  ) + c21*G(x  ,y  ) + c22*B(x  ,y  );
    _v +=3D c20*R(x+1,y  ) + c21*G(x+1,y  ) + c22*B(x+1,y  );
    _v +=3D c20*R(x  ,y+1) + c21*G(x  ,y+1) + c22*B(x  ,y+1);
    _v +=3D c20*R(x+1,y+1) + c21*G(x+1,y+1) + c22*B(x+1,y+1);

    /* Then we average these four U samples that have been
     *  accumulated */
    *v =3D _v/4;

    /* Then we update y, u, v pointers so they point to the following
     * block of 2x2 pixels */
     y +=3D 2; /* we computed 2 pixels on the horizontal direction */
     u +=3D 1;
     v +=3D 1;

  }

}

Hope this  helps, but  next time google  a bit  and you'll find  such an
explanation with even images to explain this better.

NB: the code is probably buggy, but this show you the overal idea...

--
Edouard Gomez

--dTy3Mrz/UPE2dbVg
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)

iD8DBQE+JxiHR5dTYz5sWMcRApF/AKDS1DYxon/VRsf5VRm/8m/lgAVYPgCg10ct
HwemWPU7nJ9xJBlHIkN2X9I=
=S/KO
-----END PGP SIGNATURE-----

--dTy3Mrz/UPE2dbVg--