1

I want to convert an image from RGBA to YUV422 format. Below is my code:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace cv;
using namespace std;

#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X)
#define RGB2Y(R, G, B) CLIP(( (  66 * (R) + 129 * (G) +  25 * (B) + 128) >> 8) +  16)
#define RGB2U(R, G, B) CLIP(( ( -38 * (R) -  74 * (G) + 112 * (B) + 128) >> 8) + 128)
#define RGB2V(R, G, B) CLIP(( ( 112 * (R) -  94 * (G) -  18 * (B) + 128) >> 8) + 128)

int main(int argc, char *argv[])
{

    Mat in_img, in_RGB, out_yuv;
    in_img = imread(argv[1],1);
    if(!in_img.data)
    {
        printf("Failed to load the image ... %s\n!", argv[1]);
        return -1;
    }
    short imgwidth  = in_img.cols;
    short imgheight = in_img.rows;

    cvtColor(in_img, in_RGB, CV_BGR2RGB);  
    out_yuv.create(imgheight, imgwidth, CV_16U);

    imwrite("RGB.jpg",in_RGB);

    for(int i = 0; i< imgheight; i++)
        for(int j = 0; j< imgwidth; j = j+2) {
            unsigned int val = in_RGB.at<unsigned int>(i,j);

/*********** for 1st pixel ***********/
            unsigned int tmp = val;
            unsigned char B = (unsigned char)(((tmp<<16) >> 24) | 0x000000ff ); //Extracting B channel data

            tmp = val;
            unsigned char G = (unsigned char)(((tmp<<8) >> 24) | 0x000000ff ); //Extracting G channel data

            tmp = val;
            unsigned char R = (unsigned char)((tmp >> 24) | 0x000000ff );  //Extracting R channel data

            unsigned char Y1 = RGB2Y(R, G, B);
            unsigned char U = RGB2U(R, G, B);
            unsigned char V = RGB2V(R, G, B);

/*********** for 2nd pixel ***********/
            unsigned int val1 = in_RGB.at<unsigned int>(i,j+1);
            unsigned int tmp1 = val1;
            unsigned char B1 = (unsigned char)(((tmp1<<16) >> 24) | 0x000000ff );

            tmp1 = val1;
            unsigned char G1 = (unsigned char)(((tmp1<<8) >> 24) | 0x000000ff );

            tmp1 = val1;
            unsigned char R1 = (unsigned char)((tmp1 >> 24) | 0x000000ff );

            unsigned char Y2 = RGB2Y(R, G, B);

/********** writing into out image in U-Y1-V-Y2 format *********************/

        unsigned short p1 = ((U << 8) | Y1) ;
        unsigned short p2 = ((V << 8) | Y2) ;
        out_yuv.at<unsigned short>(i,j) = p1;
        out_yuv.at<unsigned short>(i,j+1) = p2;

    }
        imwrite("YUV422.png",out_yuv);
return 0;   
}

But am not getting a proper output image. I also tried writing it in Y1-U-Y2-V format, still same output. What could be wrong with the above code or is it the output image format(.png/.jpg) that's creating the probelm? (cv::imwrite doesn't allow saving in .yuv format)

Goutham
  • 53
  • 1
  • 8
  • if the image is RGBA I don't see where you're handling/skipping the alpha channel – EdChum Jun 14 '16 at 09:55
  • `imwrite("YUV422.png",out_yuv);` What do you use to view the resulting image? – UmNyobe Jun 14 '16 at 10:00
  • @EdChum : I am ignoring the alpha channel (LSB 8 bits) and extracting 'R', 'G', 'B' channels (MSB 24 bits). – Goutham Jun 14 '16 at 10:06
  • @UmNyobe : Iam using the default image viewer available in linux. – Goutham Jun 14 '16 at 10:09
  • 1
    You are using the OR (`|`) operator for selecting bits to keep in the result, but you need to use AND for this (`& `). Setting an unsigned char to `x | 0xff` always resuts in 0xff (255), and the resulting RGB bitmap will be pure white. – Christopher Oicles Jun 14 '16 at 10:23
  • @ Christopher Oicles : Thank you for pointing that error. I have corrected it in my code. But still the output image(when saved as .jpg) is not as expected. Saving it in .png gave a little glimpse of image (though not really the desired output). – Goutham Jun 14 '16 at 10:34
  • The default image viewer doesn't support yuv. And just writing it as png will not magically solve any issue. Use [special yuv viewers](https://sourceforge.net/projects/raw-yuvplayer/) and dump the data raw in a binary file. – UmNyobe Jun 14 '16 at 10:51

1 Answers1

1
out_yuv.create(imgheight, imgwidth, CV_16U);
...
imwrite("YUV422.png",out_yuv);

Several issues here :

  • imwrite considers CV_16U to be mono channel.
  • You cannot write yuv inside png. You have to dump the data raw, and use a special yuv viewer such as raw yuvplayer to make sure your data is correct.

Edit:
Note that raw pixels acronyms are kinda big endian, ie the leftmost value is the least significant byte in memory (most of the time, video people love confusion)

  • RGBA means R = RGB[0], G = RGB[1], B = RGB[2], A = RGB[3]
  • YUYV means in a buffer p means p[0] is Y0, p[1] is U, p[2] is Y1, p[3] is V

You are doing the exact opposite with your shifts:)

Edit 2: If your image is still scrambled, you seem to be willing to write following the planar format (YUV422P akaYV16). Planar format means each channel is on a separate, contiguous plane. It is clearer to use a plain old buffer for the target data(unsigned char*, or std::vector<unsigned char>).

unsigned char* out_yuv_ptr = new unsigned char[imgwidth*imgheight*2];
unsigned char* y_ptr = out_yuv_ptr;
unsigned char* u_ptr = y_ptr + imgwidth*imgheight;
unsigned char* v_ptr = u_ptr + imgwidth*imgheight/2;
...
*y_ptr = Y1;
++y_ptr
*y_ptr = Y2;
++y_ptr
*u_ptr = U;
++u_ptr;
*v_ptr = V;
++v_ptr;
UmNyobe
  • 22,539
  • 9
  • 61
  • 90
  • Thank you for your suggestion, it was really helpful in a way. I dumped the data into a '.yuv' file using fwrite(). When checked using [raw yuvplayer](https://sourceforge.net/projects/raw-yuvplayer/), it is showing kind of scrambled output ( with proper image size and color encoding(YUV422) set in the player). If i change the 'color' option in the player to YUYV/UYVY, am getting output image as expected, except with different colors than original. Now i begin to doubt whether the conversion methodology i followed in the code is correct. Is there a way to know this? – Goutham Jun 14 '16 at 13:32
  • I have corrected the endian type and now the output seems perfect in **YUYV** format, but my original intention was to convert it into YUV422 format (which is still scrambled). Where did i go off the path from YUV422 to YUYV? – Goutham Jun 15 '16 at 13:43
  • YUV422 is not a specific format, but a category. From your comment It looks like you wanted YUYV, which is packed. Which one do you want? The planar version? see *http://www.fourcc.org/yuv.php* for the list. And what exactly is going to use that data. That may help – UmNyobe Jun 15 '16 at 13:57
  • But in the "yuvplayer", under 'color' category, there are 2 different options(among many other) for **YUYV** and **YUV422**. I have a reference '.yuv' which when opened in the player, with YUV422 option selected, displays correctly. I need to convert my RGBA image into that exact format (YUV422 as seen in the player, not YUYV). I don't think it is a planar format? According to the link you provided, my understanding is that, YUYV also comes under YUV422(packed) family. Please correct me if am wrong. – Goutham Jun 15 '16 at 14:59
  • 1
    Awesome....Now the output is perfect !!!. Thanks a lot for all your suggestions :) – Goutham Jun 16 '16 at 05:02