pythonopencvimage-processinggrayscaleimread

How are the 2 gray scale images of the same image different?


What is the difference between reading an image as grayscale and converting 3-channel image into a grayscale image?

To make it clear, if I read an image as follows:

gray_1 = cv2.imread("model.jpg", 0)

colored = cv2.imread("model.jpg")

gray_2 = cv2.cvtColor(colored, cv2.COLOR_RGB2GRAY)

print(gray_1.shape) #(1152,1536)
print(gray2.shape)  #(1152, 1536)

Now, if I check the equality of the two numpy arrays gray_1 and gray_2, they are not equal.

np.array_equal(gray_1, gray_2)

The above statement returns False. Why is that? What is the difference between gray_1 and gray_2 ?


Solution

  • Please note that this answer does not state, and has never stated, there is no difference between loading in greyscale versus loading in colour and subsequently converting to greyscale. It merely states that:

    1) OP would need to use cv2.COLOR_BGR2GRAY rather than cv2.COLOR_RGB2GRAY to make a correct comparison, and

    2) the difference is likely negligible for anyone who is prepared to use lossy JPEG to store their images.


    OpenCV natively stores in BGR order, so the true comparison would actually be:

    gray_2 = cv2.cvtColor(colored, cv2.COLOR_BGR2GRAY)
    

    as opposed to using cv2.COLOR_RGB2GRAY.


    It might be helpful to quantify "how different" the two images are as a result of being loaded directly in greyscale versus being loaded in colour and then subsequently converted, so I calculated the following statistics:

    If you use the standard 512x512 Lena image, and compare the image loaded directly as greyscale with the image loaded as colour and subsequently converted, you will get these results:

    AE: 139
    PAE: 4
    MAE: 0.00072479248046875
    MSE: 0.001220703125
    RMSE: 0.034938562148434216
    

    So, of the 262,144 pixels, only 139 pixels differ and the maximum difference between any two pixels is just 4 on a range of 0..255, i.e. less than 1.6%

    By comparison, if you compare the Lena image saved with JPEG quality of 90 versus quality 89, you get the following difference:

    AE: 158575
    PAE: 13
    MAE: 0.9766883850097656
    MSE: 2.2438392639160156
    RMSE: 1.4979450136490378
    

    So, I am saying that a 1% difference in JPEG quality causes 100x as many pixels to differ by up to 3x as much. So, the fact you choose to store your data as JPEG has a massively larger impact than the difference in the two greyscale conversion methods, and if you really care about accuracy you should rather use PNG/TIFF/PPM or some other lossless format.


    #!/usr/bin/env python3
    
    import math
    import numpy as np
    from PIL import Image
    import cv2
    
    def compare(im1, im2, metric):
       """
       Compare two images in terms of given metric.
       'AE'   Absolute Error. Simply the number of pixels that are different.
       'PAE'  Peak Absolute Error. The largest absolute difference between two corresponding pixels.
       'MAE'  Mean Absolute Error. The average difference between correspondng pixels.
       'MSE'  Mean Squared Error.
       'RMSE' Root Mean Squared Error.
       """
    
       assert(im1.shape==im2.shape)
    
       im1 = np.ravel(im1).astype(np.int64)
       im2 = np.ravel(im2).astype(np.int64)
    
       if metric == 'AE':
          # Return count of pixels that differ
          res = (im1 != im2).sum()
          return res
    
       if metric == 'PAE':
          # Return largest absolute difference
          res = np.abs((im1-im2)).max()
          return res
    
       if metric == 'MAE':
          # Return average absolute difference between corresponding pixels
          res = np.abs((im1-im2)).mean()
          return res
    
       # Calculate mean squared difference between corresponding pixels
       res = ((im1-im2)*(im1-im2)).mean()
    
       if metric == 'MSE':
          return res
    
       if metric == 'RMSE':
          return math.sqrt(res)
    
    
    # Uncomment any one of the three following blocks
    
    # Create greyscale image 640x480 filled with mid-grey
    #w,h = 640,480
    #im1 = np.zeros([h,w,1], dtype=np.uint8) + 128
    #im2 = im1.copy()
    #im2[1,1]=7
    
    # Load first image as greyscale, second as colour but then convert to greyscale afterwards
    #gray_1   = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
    #coloured = cv2.imread('lena.jpg',cv2.IMREAD_COLOR)
    #gray_2   = cv2.cvtColor(coloured, cv2.COLOR_BGR2GRAY)
    
    # Load Lena in 89 and 90 JPEG quality
    gray_1   = cv2.imread('lena89.jpg',cv2.IMREAD_GRAYSCALE)
    gray_2   = cv2.imread('lena90.jpg',cv2.IMREAD_GRAYSCALE)
    
    res = compare(gray_1, gray_2, 'AE')
    print('AE: {}'.format(res))
    res = compare(gray_1, gray_2, 'PAE')
    print('PAE: {}'.format(res))
    res = compare(gray_1, gray_2, 'MAE')
    print('MAE: {}'.format(res))
    res = compare(gray_1, gray_2, 'MSE')
    print('MSE: {}'.format(res))
    res = compare(gray_1, gray_2, 'RMSE')
    print('RMSE: {}'.format(res))