Converting RGB into LMS model using C++ and OpenCV

2020-07-30 01:01发布

问题:

I'm trying to convert image with RGB colorspace into LMS model. The transformation matrix value I got from this paper. I read this Question : RGB to LMS color space conversion with OpenCV, we referred to the same paper and I already followed all the instructions in the answer.

To make sure my codes do well, I convert the original RGB into LMS, then convert the LMS back into RGB using inverse matrix of the first matrix, and see if the output matches the original image.

But the output doesn't match the original image source. Here's my code : [UPDATED]

void test(const Mat &original, Mat &pic, int rows, int cols)
{
    for (int i = 0; i < original.rows; i++) {
        for (int j = 0; j < original.cols; j++) {
        //RGB into LMS
            pic.at<Vec3b>(i, j)[0] =    original.at<Vec3b>(i, j)[0] * 1.4671 + 
                                        original.at<Vec3b>(i, j)[1] * 0.1843 + 
                                        original.at<Vec3b>(i, j)[2] * 0.003;   //B-->S
            pic.at<Vec3b>(i, j)[1] =    original.at<Vec3b>(i, j)[0] * 3.8671 + 
                                        original.at<Vec3b>(i, j)[1] * 27.1554 + 
                                        original.at<Vec3b>(i, j)[2] * 3.4557;   //G-->M
            pic.at<Vec3b>(i, j)[2] =    original.at<Vec3b>(i, j)[0] * 4.1194 + 
                                        original.at<Vec3b>(i, j)[1] * 43.5161 + 
                                        original.at<Vec3b>(i, j)[2] * 17.8824;  //R-->L


            //LMS back into RGB
            pic.at<Vec3b>(i, j)[0] = pic.at<Vec3b>(i, j)[0] * 0.6935 + pic.at<Vec3b>(i, j)[1] * -0.0041 + pic.at<Vec3b>(i, j)[2] * -0.0004;   //S-->B
            pic.at<Vec3b>(i, j)[1] = pic.at<Vec3b>(i, j)[0] * -0.1136 + pic.at<Vec3b>(i, j)[1] * 0.0540 + pic.at<Vec3b>(i, j)[2] * -0.0102;   //M-->G
            pic.at<Vec3b>(i, j)[2] = pic.at<Vec3b>(i, j)[0] * 0.1167 + pic.at<Vec3b>(i, j)[1] * -0.1305 + pic.at<Vec3b>(i, j)[2] * 0.0809;   //L-->R

        }
    }
}

Main

int main(int argv, char** argc){
Mat original= imread("original.png", CV_LOAD_IMAGE_COLOR);
int rows = original.rows;
int cols = original.cols;

Mat pic(rows, cols, CV_8UC3);

test(original, pic, rows, cols);
imwrite("pic.png", pic);
}

Any suggestion ? Any help will be appreciated.

回答1:

Things to note:

  1. OpenCV orders the channel in the order BGR, not RGB.
  2. The values for the new channels are a weighted sum of values from all 3 channels of the previous channels.

Therefore, your conversions are incorrect. Consider the following code for obtaining channel S,

//B-->S    
pic.at<Vec3b>(i, j)[0] = original.at<Vec3b>(i, j)[0] * (0.0030, 0.1843, 1.4671);

It should be

pic.at<Vec3b>(i, j)[0] = original.at<Vec3b>(i, j)[0] * 1.4671 + 
                         original.at<Vec3b>(i, j)[1] * 0.1843 + 
                         original.at<Vec3b>(i, j)[2] * 0.03

The conversions for the other channels (i.e., L and M) should also be changed accordingly.

In general, if the color conversion cannot be performed by cvtColor, then you should consider performing it using matrix multiplication.

That is, store the conversion matrices as Mat objects, then multiply it with the Mat object of the image to get the desired result. This will be much faster than performing the conversion using for loops as OpenCV will likely be using optimized BLAS routines behind the scene to perform the multiplication. Also, the code will be more compact and clean.

The answer in this post should give you an idea of how to implement this.

UPDATED

Following your updates, your bug is that when you are converting back from LSM to BGR, pic.at<Vec3b>(i, j)[0] gets stored the value of pixel of the B channel, so it no longer contains the value of the L channel. Then, you proceed to use this value when you compute the value of the G and R channel.

Similarly, when you compute the value of the R channel, pic.at<Vec3b>(i, j)[0] and pic.at<Vec3b>(i, j)[1] are no longer the values of the S and M channels.

Note that a channel in a BGR image is a mixture of the values of all 3 LMS channels and vice versa.

You should create a new matrix for each new output (LMS image and the final BGR image) and store the values accordingly. That should fix your bug.

I mentioned -0.0003678 in my post because this is the more precise value for that entry. You get this value by inverting your original conversion matrix.