AnsweredAssumed Answered

YUV422->RGB24 - which conversion formula does IPU use?

Question asked by Smriti Verma on Jul 30, 2014
Latest reply on Jul 30, 2014 by Yuri Muhin

Hi,

 

I created a 800x600 RGB24 buffer filled with 0x80 i.e; (R, G, B) =  (128, 128, 128) or 0x808080 . I then use  IPU to convert RGB24->YUV422 (see below for the code snippet). As expected the resultant buffer has YUV= 0x808080.

Converting back to RGB24, however, has unexpected result. The resulting RGB buffer has (R, G, B) =  (130, 128, 127) or 0x82807F.

 

Which conversion does IPU apply?

 

Based on Wikipedia's YUV page, RGB should either be 0x808080 (ITU-R version) or 0x828282 (if IPU assumes YCbCr format for YUV->RGB conversion).

 

Any input is greatly appreciated.

 

Thanks!

 

YUV - Wikipedia, the free encyclopedia

Excerpt from Wikipedia:

These formulae are based on the NTSC standard;

Y' =  0.299 \times R + 0.587 \times G + 0.114 \times B
U  = -0.147 \times R - 0.289 \times G + 0.436 \times B
V  =  0.615 \times R - 0.515 \times G - 0.100 \times B

On older, non-SIMD architectures, floating point arithmetic is much slower than using fixed-point arithmetic, so an alternative formulation is:

C = Y' - 16
D = U - 128
E = V - 128

Using the previous coefficients and noting that clamp() denotes clamping a value to the range of 0 to 255, the following formulae provide the conversion from Y'UV to RGB (NTSC version):

R = \mathrm{clamp}(( 298 \times C                + 409 \times E + 128) >> 8)
G = \mathrm{clamp}(( 298 \times C - 100 \times D - 208 \times E + 128) >> 8)
B = \mathrm{clamp}(( 298 \times C + 516 \times D                + 128) >> 8)

Note: The above formulae are actually implied for YCbCr. Though the term YUV is used here, it should be noted that YUV and YCbCr are not exactly the same in a strict manner.

The ITU-R version of the formulae is different:

Y  = 0.299 \times R + 0.587 \times G + 0.114 \times B + 0
Cb = -0.169 \times R - 0.331 \times G + 0.499 \times B + 128
Cr = 0.499 \times R - 0.418 \times G - 0.0813 \times B + 128
R = \mathrm{clamp}(Y + 1.402 \times (Cr - 128))
G = \mathrm{clamp}(Y - 0.344 \times (Cb - 128) - 0.714 \times (Cr - 128))
B = \mathrm{clamp}(Y + 1.772 \times (Cb - 128))
Code snippet:

  // set task basics

  memset( &ipuTask, 0, sizeof(ipuTask) );

  ipuTask.input.width = inW;

  ipuTask.input.height = inH;

  ipuTask.input.format = inFmt;

  ipuTask.output.width = outW;

  ipuTask.output.height = outH;

  ipuTask.output.format = outFmt;

 

 

  // get image file and check for failure

 

 

 

  // compute input size in bytes

  iInputSize = ipuTask.input.width * ipuTask.input.height *

               computeBPP( ipuTask.input.format ) / 8;

 

 

  // set the amount of memory needed for the task input

  ipuTask.input.paddr = iInputSize;

 

 

  // allocate memory for the input image and check for failure

  // (NOTE: input.paddr will be replaced with the physical address)

  iRetValue = ioctl( iIPUFD, IPU_ALLOC, &ipuTask.input.paddr );

  if( iRetValue < 0 )

  {

    perror( "ioctl(IPU_ALLOC)" );

    Cleanup();

    return iRetValue;

  }

 

 

  // map virtual address to input memory

  pInputBuff = mmap( 0, iInputSize, PROT_READ | PROT_WRITE,

                     MAP_SHARED, iIPUFD, ipuTask.input.paddr );

  if( !pInputBuff || pInputBuff == MAP_FAILED )

  {

    perror( "mmap" );

    Cleanup();

    return -1;

  }

 

 

  // compute output size in bytes

  iOutputSize = ipuTask.output.width * ipuTask.output.height *

                computeBPP( ipuTask.output.format ) / 8;

 

 

  // set the amount of memory needed for the task output

  ipuTask.output.paddr = iOutputSize;

 

 

  // allocate memory for the output image and check for failure

  // (NOTE: output.paddr will be replaced with the physical address)

  iRetValue = ioctl( iIPUFD, IPU_ALLOC, &ipuTask.output.paddr );

  if( iRetValue < 0 )

  {

    perror( "ioctl(IPU_ALLOC)" );

    Cleanup();

    return iRetValue;

  }

 

 

  // map virtual address to output memory

  pOutputBuff = mmap( 0, iOutputSize, PROT_READ | PROT_WRITE,

                      MAP_SHARED, iIPUFD, ipuTask.output.paddr );

  if( !pOutputBuff || pOutputBuff == MAP_FAILED )

  {

    perror( "mmap" );

    Cleanup();

    return -1;

  }

 

 

  // get output file and check for failure

  fOutput = fopen( outName.c_str(), "wb" );

  if( fOutput < 0 )

  {

    cerr << "failed to open " << outName.c_str() << endl;

    Cleanup();

    return -1;

  }

 

  // read input image

  iRetValue = fread( pInputBuff, 1, iInputSize, fInput );

  if( iRetValue < iInputSize )

  {

       perror( "fread" );

       Cleanup();

       return -1;

  }

 

  // execute the IPU task and check for failure

  iRetValue = ioctl( iIPUFD, IPU_QUEUE_TASK, &ipuTask );

Outcomes