OpenCV convert RGB to YCbCr and vice versa by hand

2020-07-27 01:53发布

问题:

i want to convert my rgb image into a ycbcr image and vice versa by hand. But there is an mistake and I can't find it.

My images don't look like they should. Here is my code:

void YCbCrToRGB(IplImage* ScrY, IplImage* ScrCb, IplImage* ScrCr, IplImage* DesR, IplImage* DesG, IplImage* DesB){

    for(int i=0; i < ScrY->height; i++){
        for(int j=0; j < ScrY->width; j++){
            double Y = (double)(ScrY->imageData + ScrY->widthStep*i)[j];
            double Cb = (double)(ScrCb->imageData + ScrCb->widthStep*i)[j];
            double Cr = (double)(ScrCr->imageData + ScrCr->widthStep*i)[j];

            Cb = Cb -128;
            Cr = Cr -128;
            int r;
            int g;
            int b;

            (DesR->imageData + DesR->widthStep*i)[j] = (int)(1 * Y + 0 * Cb + 1.4 * Cr);
            (DesG->imageData + DesG->widthStep*i)[j] = (int)(1 * Y - 0.343 * Cb - 0.711 *Cr);
            (DesB->imageData + DesB->widthStep*i)[j] = (int)(1* Y + 1.765 * Cb + 0* Cr);
    }}

void RGBtoYCbCr(IplImage* ScrR, IplImage* ScrG, IplImage* ScrB, IplImage* DesY, IplImage* DesCb, IplImage* DesCr){
    for(int i=0; i < ScrR->height; i++){
        for(int j=0; j < ScrR->width; j++){
            double R = (double)(ScrR->imageData + ScrR->widthStep*i)[j];
            double G = (double)(ScrG->imageData + ScrG->widthStep*i)[j];
            double B = (double)(ScrB->imageData + ScrB->widthStep*i)[j];

            (DesY->imageData + DesY->widthStep*i)[j]   =       0.299    *R + 0.587  *G  + 0.114  *B;
            (DesCb->imageData + DesCb->widthStep*i)[j] =      -0.1687   *R - 0.3313 *G  + 0.5    *B + 128;
            (DesCr->imageData + DesCr->widthStep*i)[j] =       0.5      *R - 0.4187 *G  - 0.0813 *B + 128;          
        }
    }}

int _tmain(int argc, _TCHAR* argv[])
{
    try {
        IplImage* img = cvLoadImage( "C:\\sad-cat.jpg",1 ); 
        cvNamedWindow( "Example1", CV_WINDOW_AUTOSIZE ); 
        cvShowImage( "Example1", img );

        IplImage *r = cvCreateImage(cvGetSize(img), img->depth, 1);
        IplImage *g = cvCreateImage(cvGetSize(img), img->depth, 1);
        IplImage *b = cvCreateImage(cvGetSize(img), img->depth, 1);

        cvSplit(img, b, g, r, NULL);

        IplImage *y  = cvCreateImage(cvGetSize(img), img->depth, 1);
        IplImage *cb = cvCreateImage(cvGetSize(img), img->depth, 1);
        IplImage *cr = cvCreateImage(cvGetSize(img), img->depth, 1);


        RGBtoYCbCr(r,g,b,y,cb,cr);

        IplImage *ycbcr = cvCreateImage(cvGetSize(img), img->depth, 3);
        cvMerge(y,cb,cr,NULL,ycbcr);

        cvNamedWindow( "YCbCr from RGB", CV_WINDOW_AUTOSIZE ); 
        cvShowImage( "YCbCr from RGB", ycbcr );



        YCbCrToRGB(y,cb,cr,r,g,b);

        IplImage *RGBfromYCbCr = cvCreateImage(cvGetSize(img), img->depth, 3);
        cvMerge(r,g,b,NULL,RGBfromYCbCr);

        cvNamedWindow( "RGB from YCbCr", CV_WINDOW_AUTOSIZE ); 
        cvShowImage( "RGB from YCbCr", RGBfromYCbCr );

        cvWaitKey(0);
        return 0;
    }
    catch( exception& e){
        std::cout<<("An error occurred.") << std::endl;
        std::cout << e.what() <<std::endl; // Print the error message. 
         cvWaitKey(0);
    }  
}

Here is the Link with syntax highlighting : http://paste2.org/p/1569904

I think its a casting error or something like this. THANK YOU VERY MUCH

PS: See comments for formula and example picture (i am not allowed to post more than two links or images)

回答1:

Yes, it is a casting issue.

        double Y = (double)(ScrY->imageData + ScrY->widthStep*i)[j];
        double Cb = (double)(ScrCb->imageData + ScrCb->widthStep*i)[j];
        double Cr = (double)(ScrCr->imageData + ScrCr->widthStep*i)[j];

should be

        double Y = (uchar)(ScrY->imageData + ScrY->widthStep*i)[j];
        double Cb = (uchar)(ScrCb->imageData + ScrCb->widthStep*i)[j];
        double Cr = (uchar)(ScrCr->imageData + ScrCr->widthStep*i)[j];

Make the same change in the first three lines of RGBtoYCbCr.

Also, you have rounding errors. You should round the results before assigning:

        (DesY->imageData + DesY->widthStep*i)[j]   = round(     0.299    *R + 0.587  *G  + 0.114  *B);
        (DesCb->imageData + DesCb->widthStep*i)[j] = round(    -0.1687   *R - 0.3313 *G  + 0.5    *B) + 128;
        (DesCr->imageData + DesCr->widthStep*i)[j] = round(     0.5      *R - 0.4187 *G  - 0.0813 *B) + 128;          

Lastly, cvMerge(r,g,b,NULL,RGBfromYCbCr); should be cvMerge(b,g,r,NULL,RGBfromYCbCr); because cvShowImage assumes BGR, not RGB.



回答2:

Well, my answer is maybe stupid but you should know that cvShowImage() interpretes what it reads as BGR images. If you convert to YCbCr and try to look at what the YCbCr looks, for sure it will look strange as it will be :

  • Y interpreted as B channel
  • Cb interpreted as G channel
  • Cr interpreted as R channel

I don't know which test you ran over your code, the best final test would be to read the image from : YCbCrToRGB( ) of your RGBtoYCbCr( )

  • your image => RGBToYCbCr() => YCbCrToRGB() => cvShowImage()

Second piece of advice, check if you write BGR images if not you'll need to convert from RGB to BGR...

Tell me what you obtain, and if it works or not,

Julien