1

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. enter image description here

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.

raisa_
  • 594
  • 2
  • 10
  • 28
  • I keep getting a red image as a result when converting from BGR to RGB to LMS and back. Did you run into this problem? – Logan Jahnke Feb 26 '19 at 03:05
  • 1
    Hi @LoganJahnke I didn't encounter the same problem. Also, I didn't convert from BGR to RGB first before LMS. You can just convert BGR directly to LMS by switching the LMS matrix transformation (LMS --> SML). – raisa_ Feb 26 '19 at 03:13
  • Okay, I actually used an answer from a very similar question on a different site. The answer suggested using the method `transform()`. However, I saw your other post now where you use `reshape()`. I may have to try that. – Logan Jahnke Feb 26 '19 at 03:17

1 Answers1

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.

lightalchemist
  • 10,031
  • 4
  • 47
  • 55
  • Hi, thanks for the correction and recommendation ! I'm aware about the BGR order. I've also been working using Mat image source and I do store the matrices as Mat object. But I still couldn't get the expected result (please see my updated code). I'm interested in using reshape, but I'm curious to make this work before that. – raisa_ Jan 27 '18 at 07:47
  • Hi, thanks for update. I tried to use 2 different functions to convert RGB - LMS, and LMS - RGB but it seems like I still have difficulty in parsing the value. I'll update again when I have progress. I also have checked the entry for the inverse matrix using Matlab. It seems like the value -0.0004 is correct already. – raisa_ Jan 28 '18 at 13:56
  • Hi, thanks ! Now I completely understand the missing part in my variable storing issue and have fixed it. And just to make sure.. because the result is still different from the original, is transformation from RGB-LMS, and then LMS back to RGB reversible ? Like, we can actually get the same output with the original ? – raisa_ Jan 30 '18 at 17:29
  • There will be some minor differences due to the rounding errors, but perceptually they should be the same. That is, the RGB image you obtained by converting from LMS back to RGB should *look* the same as the original. If not, then there are probably other problems with your code. – lightalchemist Jan 30 '18 at 19:02
  • Thanks for the information! Gonna try with the reshape now. – raisa_ Jan 30 '18 at 19:42