Search code examples
pythonopencvimage-processingmatrixcolor-space

Converting a RGB image to LMS, and vice versa, using OpenCV


I'm trying to convert an image from RGB to LMS -and vice versa- using OpenCV in Python. From what I understand, I am supposed to use a given 3x3 transformation matrix and multiply it to a 3x1 RGB/LMS matrix. The transformation matrices used can be found here.

I've explored previously asked questions on this site but unfortunately they're in C++, a language I have yet to be proficient in and I have difficulty in understanding how exactly they've solved their problems.

Here is my code so far: [Solved as of 2019-05-19]

import numpy as np
import cv2
#Transformation Matrix#
MsRGB  = np.zeros((3,3), dtype='float')
MHPE   = np.zeros((3,3), dtype='float')

MsRGB = np.array([[0.4124564, 0.3575761, 0.1804375],
                  [0.2126729, 0.7151522, 0.0721750],
                  [0.0193339, 0.1191920, 0.9503041]])

MHPE = np.array([[ 0.4002, 0.7076, -0.0808],
                 [-0.2263, 1.1653,  0.0457],
                 [      0,      0,  0.9182]])

Trgb2lms = MHPE @ MsRGB
Tlms2rgb = np.linalg.inv(Trgb2lms)

imgpath = "(insert file directory here)"
imgIN = cv2.imread(imgpath,cv2.IMREAD_UNCHANGED)
imgINrgb = cv2.cvtColor(imgIN, cv2.COLOR_BGR2RGB)

x,y,z = imgINrgb.shape
imgLMS = np.zeros((x,y,z), dtype='float')

imgReshaped = imgINrgb.transpose(2, 0, 1).reshape(3,-1)
imgLMS = Trgb2lms @ imgReshaped #Convert to LMS
imgOUT = Tlms2rgb @ imgLMS #Convert back to RGB

imgLMS = imgLMS.reshape(z, x, y).transpose(1, 2, 0).astype(np.uint8)
imgOUT = imgOUT.reshape(z, x, y).transpose(1, 2, 0).astype(np.uint8)

imgOUT = cv2.cvtColor(imgOUT, cv2.COLOR_RGB2BGR)
cv2.imshow('Input', imgIN)
cv2.imshow('LMS', imgLMS)
cv2.imshow('Output', imgOUT)
cv2.waitKey(0)
cv2.destroyAllWindows()

The code is now able to perform linear transformation on a given RGB image using a given transformation matrix. Results can be found here.


Solution

  • There are a few errors given the context of your question:

    1. T is not defined. Judging from the context of your code, this should be Trgb2lms instead so we need to change those.

    2. From what I can gather from the question, you are applying a linear transformation to all pixels in the image. To do this, you want to reshape the matrix so that we have three rows where each row corresponds to a single pixel followed by an unravelling of all pixels along the columns. In that case, the reshape method is incorrect. You need not only shuffle the dimensions so that the last dimension is first but you'll also need to set the last dimension of the reshape so that it's -1. This means that we will automatically fill up the columns so that it contains the total number of pixels in the image.

    3. Finally, once you do the linear transformation, you need to reshape the matrix back to the original image size. You can use a final reshape call and use x, y and z from the original call you made to infer the image dimensions. Remember that when we reshape, the channels come first so we'll have to permute the dimensions again. You'll also want to go back to unsigned 8-bit precision after we do the transformation.

    4. Also to compare, let's run this through the inverse transformation to make sure we have the original.

    Therefore:

    import numpy as np
    import cv2
    #Transformation Matrix#
    MsRGB  = np.zeros((3,3), dtype='float')
    MHPE   = np.zeros((3,3), dtype='float')
    
    MsRGB = np.array([[0.4124564, 0.3575761, 0.1804375],
                      [0.2126729, 0.7151522, 0.0721750],
                      [0.0193339, 0.1191920, 0.9503041]])
    
    MHPE = np.array([[ 0.4002, 0.7076, -0.0808],
                     [-0.2263, 1.1653,  0.0457],
                     [      0,      0,  0.9182]])
    
    Trgb2lms = MHPE @ MsRGB
    
    # Change
    Tlms2rgb = np.linalg.inv(Trgb2lms)
    
    imgpath = "(insert filename here)"
    imgIN = cv2.imread(imgpath,cv2.IMREAD_UNCHANGED)
    imgINrgb = cv2.cvtColor(imgIN, cv2.COLOR_BGR2RGB)
    
    x,y,z = imgINrgb.shape
    imgLMS = np.zeros((x,y,z), dtype='float')
    
    #imgFlatten = imgINrgb.flatten()
    
    # Change
    imgReshaped = imgINrgb.transpose(2, 0, 1).reshape(3,-1)
    
    # Change
    imgLMS = Trgb2lms @ imgReshaped
    imgOUT = Tlms2rgb @ imgLMS
    
    # New
    imgLMS = imgLMS.transpose(z, x, y).permute(1, 2, 0).astype(np.uint8)
    imgOUT = imgOUT.transpose(z, x, y).permute(1, 2, 0).astype(np.uint8)