从 RGB 转换为 Lαβ 颜色空间并使用 OpenCV 将其转换回 RGB

Converting from RGB to Lαβ Color spaces and converting it back to RGB using OpenCV

我目前正在尝试在 RGB(红、绿、蓝)颜色 space 和 Lαβ 颜色 space 之间转换颜色,基于此 paper 中的详细信息。 我的困难在于扭转转换过程。当结果与初始 RGB Mat 不同时。我想我在 Mats 之间的类型转换中遗漏了一些东西,但我不知道是什么!
这是我的代码:

  <!-- 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;
}

您在函数 LAlphBeta2RGB 中有 floatuchar 截断错误:

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

您可以使用以下方法解决此问题:

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

但是,您不应明确处理转换问题。您可以使用 saturate_cast 来为您处理。您可以将 R,G,B 变量声明为 uchar:

 uchar B, G, R;

并执行转换为:

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

然后分配为:

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

或者避免使用 R,G,B 完全使用:

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

这里是完整的代码。我冒昧地使用 Mat_ 而不是 Mat 作为函数参数,以避免使用 at<type>() 访问像素值。事实上,您已经假设函数的输入分别为 CV_8UC3CV_32FC3

#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;
}