Converting between YV12 and YUY2
- From: "Jeremy Noring" <JeremyNoring@xxxxxxxxxxxxxxxxxxxxxxxxx>
- Date: Fri, 9 Dec 2005 14:03:02 -0800
I currently have a method to convert between YV12 and YUY2 that looks
something similar to this (the one in my code accounts for stride, but that's
the only difference):
// Converts an image from YV12 to YUY2. Input and output images must
// be of identical size. Function does not deal with any potential stride
// issues.
HRESULT ConvertYV12ToYUY2( char * pYV12Buffer, char * pYUY2Buffer, int
IMAGEHEIGHT, int IMAGEWIDTH )
{
if( pYUY2Buffer == NULL || pYV12Buffer == NULL || IMAGEHEIGHT < 2
|| IMAGEWIDTH < 2 )
{
return E_INVALIDARG;
}
// Let's start out by getting pointers to the individual planes in our
// YV12 image. Note that the Y plane in a YV12 image's size is
// simply the image height * image width. This is because all values
// are 8 bits. Also notice that the U and V planes are one quarter
// the size of the Y plane (hence the division by 4).
BYTE * pYV12YPlane = pYV12Buffer;
BYTE * pYV12VPlane = pYV12YPlane + ( IMAGEHEIGHT * IMAGEWIDTH );
BYTE * pYV12UPlane = pYV12VPlane + ( ( IMAGEHEIGHT * IMAGEWIDTH ) / 4
);
BYTE * pYUV2BufferCursor = pYUV2Buffer;
// Keep in mind that YV12 has only half of the U and V information that
// a YUY2 image contains. Because of that, we need to reuse the U and
// V plane values, so we only increment that buffer every other row
// of pixels.
bool bMustIncrementUVPlanes = false;
for( int ImageHeight = 0; ImageHeight < IMAGEHEIGHT; ImageHeight++ )
{
// Two temporary cursors for our U and V planes, which are the weird
ones to deal with.
BYTE * pUCursor = pYV12UPlane;
BYTE * pVCursor = pYV12VPlane;
// We process two pixels per pass through this equation,
// hence the (IMAGEWIDTH/2).
for( int ImageWidth = 0; ImageWidth < ( IMAGEWIDTH / 2 ) ;
ImageWidth++ )
{
// first things first: copy our Y0 value.
memcpy( pYUY2BufferCursor, pYV12YPlane, 1 );
pYUY2BufferCursor++;
pYV12YPlane++;
// Copy U0 value
memcpy( pYUY2BufferCursor, pUCursor, 1 );
pYUY2BufferCursor++;
pUCursor++;
// Copy Y1 value
memcpy( pYUY2BufferCursor, pYV12YPlane, 1 );
pYUY2BufferCursor++;
pYV12YPlane++;
// Copy V0 value
memcpy( pYUY2BufferCursor, pVCursor, 1 );
pYUY2BufferCursor++;
pVCursor++;
}
// Since YV12 has half the UV data that YUY2 has, we reuse these
// values--so we only increment these planes every other pass
// through.
if( bMustIncrementUVPlanes )
{
pYV12VPlane += IMAGEWIDTH / 2;
pYV12UPlane += IMAGEWIDTH / 2;
bMustIncrementUVPlanes = false;
}
else
{
bMustIncrementUVPlanes = true;
}
}
return S_OK;
}
Basically, because both image formats are 8-bit YUV, all values are
equivalent and I don't see a reason to do any interpolation. Since the YV12
image only has half of the U and V information of the YUY2 image, I reuse the
U and V values of the YV12 image when creating the YUY2 image.
However, looking at this article:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwmt/html/YUVFormats.asp
....I noticed that they have a section on converting between 4:2:0 and 4:2:2
formats (which YV12 to YUY2 falls under), and they propose the following
solution:
Let each vertical line of input chroma samples be an array Cin[] that ranges
from 0 to N - 1. The corresponding vertical line on the output image will be
an array Cout[] that ranges from 0 to 2N - 1. To convert each vertical line,
perform the following process:
Cout[0] = Cin[0];
Cout[1] = clip((9 * (Cin[0] + Cin[1]) – (Cin[0] + Cin[2]) + 8) >> 4);
Cout[2] = Cin[1];
Cout[3] = clip((9 * (Cin[1] + Cin[2]) - (Cin[0] + Cin[3]) + 8) >> 4);
Cout[4] = Cin[2]
Cout[5] = clip((9 * (Cin[2] + Cin[3]) - (Cin[1] + Cin[4]) + 8) >> 4);
....
Cout[2*i] = Cin[i]
Cout[2*i+1] = clip((9 * (Cin[i] + Cin[i+1]) - (Cin[i-1] + Cin[i+2]) + 8) >>
4);
....
Cout[2*N-3] = clip((9 * (Cin[N-2] + Cin[N-1]) - (Cin[N-3] + Cin[N-1]) + 8)
>> 4);
Cout[2*N-2] = Cin[N-1];
Cout[2*N-1] = clip((9 * (Cin[N-1] + Cin[N-1]) - (Cin[N-2] + Cin[N-1]) + 8)
>> 4);
I don't understand why they need to do interpolation to convert between
these to formats. Why would use use the above (which is more expensive from
a CPU standpoint, and also will incur some degree of rounding errors, I
assume) to just reformatting the data you actually have?
Any video gurus out there who can answer that for me?
Thanks, and sorry for the long post.
--
My Site (under construction): http://deepsea.no-ip.com/AV/
.
- Follow-Ups:
- Re: Converting between YV12 and YUY2
- From: Tim Roberts
- Re: Converting between YV12 and YUY2
- Prev by Date: Re: How many threads of "Smart Tee" filter created
- Next by Date: Re: Crescentec dc-1100 adapter - access to its registers / SOLVED
- Previous by thread: Re: How to write Capture Buffer filter?
- Next by thread: Re: Converting between YV12 and YUY2
- Index(es):
Relevant Pages
|