Converting from RGB to Lαβ Color spaces and conver

2019-09-05 02:28发布

问题:

I am currently trying to convert colors between RGB (red, green, blue) color space and Lαβ color space, Based on the details in the this paper. My difficulties are in reversing the conversion process. When the result is not as same as initial RGB Mat. I think I missing something in type castings between Mats but I can't tell what is it!
here is my code:

  <!-- language: lang-cc -->

Mat DetectTrackFace::RGB2LAlphBeta(Mat &src)
{
    Mat dest;
    Mat L_AlphBeta(src.rows, src.cols, CV_32FC3);
    //cvtColor(src,dest,CV_BGR2XYZ);
    float X,Y,Z,L,M,S,_L,Alph,Beta;
    int R,G,B;
    for(int i = 0; i < src.rows; i++)
      {
        for(int j = 0; j < src.cols; j++)
        {
            B = src.at<Vec3b>(i, j)[0];
            G = src.at<Vec3b>(i, j)[1];
            R = src.at<Vec3b>(i, j)[2];


            X = ( 0.4124 * R ) + ( 0.3576 * G ) + ( 0.1805 * B);
            Y = ( 0.2126 * R ) + ( 0.7152 * G ) + ( 0.0722 * B);
            Z = ( 0.0193 * R ) + ( 0.1192 * G ) + ( 0.9505 * B);

            L = (0.3897 * X) + (0.6890 * Y) + (-0.0787 * Z);
            M = (-0.2298 * X) + (1.1834* Y) + (0.0464 * Z);
            S = (0.0000 * X) + (0.0000 * Y) + (1.0000 * Z);

            //for handling log
            if(L == 0.0000) L=1.0000;
            if(M == 0.0000) M = 1.0000;
            if( S == 0.0000) S = 1.0000;


            //LMS to Lab
            _L = (1.0 / sqrt(3.0)) *((1.0000 * log10(L)) + (1.0000 * log10(M)) + (1.0000 * log10(S)));
            Alph =(1.0 / sqrt(6.0)) * ((1.0000 * log10(L)) + (1.0000 * log10(M)) + (-2.0000 * log10(S)));
            Beta = (1.0 / sqrt(2.0)) * ((1.0000 * log10(L)) + (-1.0000 * log10(M)) + (-0.0000 * log10(S)));

            L_AlphBeta.at<Vec3f>(i, j)[0] = _L;
            L_AlphBeta.at<Vec3f>(i, j)[1] = Alph;
            L_AlphBeta.at<Vec3f>(i, j)[2] = Beta;
        }
    }

    return L_AlphBeta;
}

Mat DetectTrackFace::LAlphBeta2RGB(Mat &src)
{
    Mat XYZ(src.rows, src.cols, src.type());
    Mat BGR(src.rows, src.cols, CV_8UC3);

    float X,Y,Z,L,M,S,_L,Alph,Beta, B,G,R;
    for(int i = 0; i < src.rows; i++)
      {
        for(int j = 0; j < src.cols; j++)
        {
            _L = src.at<Vec3f>(i, j)[0]*1.7321;
            Alph = src.at<Vec3f>(i, j)[1]*2.4495;
            Beta = src.at<Vec3f>(i, j)[2]*1.4142;



   /*Inv_Transform_logLMS2lab =

   0.33333   0.16667   0.50000
   0.33333   0.16667  -0.50000
   0.33333  -0.33333   0.00000*/
            L = (0.33333*_L) + (0.16667 * Alph) + (0.50000 * Beta);
            M = (0.33333 * _L) + (0.16667 * Alph) + (-0.50000 * Beta);
            S = (0.33333 * _L) + (-0.33333 * Alph) + (0.00000* Beta);

            L = pow(10 , L);
            if(L == 1) L=0;
            M = pow(10 , M);
            if(M == 1) M=0;
            S = pow(10 , S);
            if(S == 1) S=0;
    /*Inv_Transform_XYZ2LMS

   1.91024  -1.11218   0.20194
   0.37094   0.62905   0.00001
   0.00000   0.00000   1.00000*/

            X = (1.91024 *L ) + (-1.11218 * M ) +(0.20194 * S);
            Y = (0.37094 * L ) + (0.62905 * M ) +(0.00001 * S);
            Z = (0.00000 * L) + (0.00000 * M ) +(1.00000 * S);
    /*Inv_Transform_RGB2XYZ
    3.240625  -1.537208  -0.498629
    -0.968931   1.875756   0.041518
    0.055710  -0.204021   1.056996*/

            R = ( 3.240625 * X) + ( -1.537208 * Y) + ( -0.498629 * Z);
            G = ( -0.968931 * X) + ( 1.875756 * Y) + ( 0.041518 * Z);
            B = ( 0.055710 * X) + ( -0.204021 * Y) + ( 1.056996 * Z);
            if(R>255) R = 255;
            if(G>255) G = 255;
            if(B>255) B = 255;
            if(R<0) R = 0;
            if(G<0) G = 0;
            if(B<0) B = 0;
            if(R > 255 || G > 255 || B > 255 || R < 0 || G < 0 || B<0)
                cout<<"R = "<<R<<" G = "<<G <<" B = "<<B<<endl;


            BGR.at<Vec3b>(i, j)[0] = (uchar)B;
            BGR.at<Vec3b>(i, j)[1] = (uchar)G;
            BGR.at<Vec3b>(i, j)[2] = (uchar)R;

        }
    }
    //normalize(BGR,BGR, 255, 0, NORM_MINMAX, CV_8UC3 );
    return BGR;
}

回答1:

You have float to uchar truncation errors in the function LAlphBeta2RGB here:

BGR.at<Vec3b>(i, j)[0] = (uchar)B;
BGR.at<Vec3b>(i, j)[1] = (uchar)G;
BGR.at<Vec3b>(i, j)[2] = (uchar)R;

You can solve this using:

BGR(i, j)[0] = uchar(cvRound(B));
BGR(i, j)[1] = uchar(cvRound(G));
BGR(i, j)[2] = uchar(cvRound(R));

However, you shouldn't take care of conversion problems explicitly. You can use saturate_cast to handle this for you. You can declare R,G,B variables as uchar:

 uchar B, G, R;

and perform the conversion as:

R = saturate_cast<uchar>((3.240625 * X) + (-1.537208 * Y) + (-0.498629 * Z));
G = saturate_cast<uchar>((-0.968931 * X) + (1.875756 * Y) + (0.041518 * Z));
B = saturate_cast<uchar>((0.055710 * X) + (-0.204021 * Y) + (1.056996 * Z));

and then assign as:

BGR(i, j)[0] = B;
BGR(i, j)[1] = G;
BGR(i, j)[2] = R;

Or avoid using R,G,B entirely using:

BGR(i, j)[2] = saturate_cast<uchar>((3.240625 * X) + (-1.537208 * Y) + (-0.498629 * Z));
BGR(i, j)[1] = saturate_cast<uchar>((-0.968931 * X) + (1.875756 * Y) + (0.041518 * Z));
BGR(i, j)[0] = saturate_cast<uchar>((0.055710 * X) + (-0.204021 * Y) + (1.056996 * Z));

Here the full code. I took the liberty to use Mat_ instead of Mat as functions arguments, to avoid using at<type>() to access pixel values. In fact, you are already assuming that inputs of your functions are CV_8UC3 and CV_32FC3, respectively.

#include <opencv2\opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

Mat RGB2LAlphBeta(Mat3b &src)
{
    Mat3f L_AlphBeta(src.rows, src.cols);
    //cvtColor(src,dest,CV_BGR2XYZ);
    float X, Y, Z, L, M, S, _L, Alph, Beta;
    int R, G, B;
    for (int i = 0; i < src.rows; i++)
    {
        for (int j = 0; j < src.cols; j++)
        {
            B = src(i, j)[0];
            G = src(i, j)[1];
            R = src(i, j)[2];


            X = (0.4124 * R) + (0.3576 * G) + (0.1805 * B);
            Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
            Z = (0.0193 * R) + (0.1192 * G) + (0.9505 * B);

            L = (0.3897 * X) + (0.6890 * Y) + (-0.0787 * Z);
            M = (-0.2298 * X) + (1.1834* Y) + (0.0464 * Z);
            S = (0.0000 * X) + (0.0000 * Y) + (1.0000 * Z);

            //for handling log
            if (L == 0.0000) L = 1.0000;
            if (M == 0.0000) M = 1.0000;
            if (S == 0.0000) S = 1.0000;


            //LMS to Lab
            _L = (1.0 / sqrt(3.0)) *((1.0000 * log10(L)) + (1.0000 * log10(M)) + (1.0000 * log10(S)));
            Alph = (1.0 / sqrt(6.0)) * ((1.0000 * log10(L)) + (1.0000 * log10(M)) + (-2.0000 * log10(S)));
            Beta = (1.0 / sqrt(2.0)) * ((1.0000 * log10(L)) + (-1.0000 * log10(M)) + (-0.0000 * log10(S)));

            L_AlphBeta(i, j)[0] = _L;
            L_AlphBeta(i, j)[1] = Alph;
            L_AlphBeta(i, j)[2] = Beta;
        }
    }

    return L_AlphBeta;
}

Mat LAlphBeta2RGB(Mat3f &src)
{
    Mat3f XYZ(src.rows, src.cols);
    Mat3b BGR(src.rows, src.cols);

    float X, Y, Z, L, M, S, _L, Alph, Beta;
    for (int i = 0; i < src.rows; i++)
    {
        for (int j = 0; j < src.cols; j++)
        {
            _L = src(i, j)[0] * 1.7321;
            Alph = src(i, j)[1] * 2.4495;
            Beta = src(i, j)[2] * 1.4142;

            /*Inv_Transform_logLMS2lab =

            0.33333   0.16667   0.50000
            0.33333   0.16667  -0.50000
            0.33333  -0.33333   0.00000*/
            L = (0.33333*_L) + (0.16667 * Alph) + (0.50000 * Beta);
            M = (0.33333 * _L) + (0.16667 * Alph) + (-0.50000 * Beta);
            S = (0.33333 * _L) + (-0.33333 * Alph) + (0.00000* Beta);

            L = pow(10, L);
            if (L == 1) L = 0;
            M = pow(10, M);
            if (M == 1) M = 0;
            S = pow(10, S);
            if (S == 1) S = 0;
            /*Inv_Transform_XYZ2LMS

            1.91024  -1.11218   0.20194
            0.37094   0.62905   0.00001
            0.00000   0.00000   1.00000*/

            X = (1.91024 *L) + (-1.11218 * M) + (0.20194 * S);
            Y = (0.37094 * L) + (0.62905 * M) + (0.00001 * S);
            Z = (0.00000 * L) + (0.00000 * M) + (1.00000 * S);
            /*Inv_Transform_RGB2XYZ
            3.240625  -1.537208  -0.498629
            -0.968931   1.875756   0.041518
            0.055710  -0.204021   1.056996*/

            BGR(i, j)[2] = saturate_cast<uchar>((3.240625 * X) + (-1.537208 * Y) + (-0.498629 * Z));
            BGR(i, j)[1] = saturate_cast<uchar>((-0.968931 * X) + (1.875756 * Y) + (0.041518 * Z));
            BGR(i, j)[0] = saturate_cast<uchar>((0.055710 * X) + (-0.204021 * Y) + (1.056996 * Z));
        }
    }
    //normalize(BGR,BGR, 255, 0, NORM_MINMAX, CV_8UC3 );
    return BGR;
}


int main()
{
    Mat3b img = imread("path_to_image");

    Mat3f labb = RGB2LAlphBeta(img);

    Mat3b rgb = LAlphBeta2RGB(labb);

    Mat3b diff;
    absdiff(img, rgb, diff);

    // Check if all pixels are equals
    cout << ((sum(diff) == Scalar(0, 0, 0, 0)) ? "Equals" : "Different");

    return 0;
}