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

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

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

1,978 Views
ssvma
Contributor I

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 );

Labels (1)
Tags (4)
0 Kudos
1 Reply

748 Views
Yuri
NXP Employee
NXP Employee

Please refer to section 37.4.5.4 (Main Processing Section), item 4 of the i.MX6 DQ Reference
Manual.

http://cache.freescale.com/files/32bit/doc/ref_manual/IMX6DQRM.pdf

"4. First color space conversion YUV to RGB or RGB to YUV with the conversion

matrix CSC1. The conversion matrix coefficients are programmable. They are stored

in the Task Parameter Memory."


Have a great day,
Yuri

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos