简体   繁体   中英

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 ?

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:

  • Absolute Error - simply the number of pixels that differ

  • Peak Absolute Error - the largest absolute difference between any two corresponding pixels

  • Mean Absolute Error - the average absolute difference between corresponding pixels

  • Mean Squared Error - the average squared difference between corresponding pixels

  • Root Mean Square Error - square root of the above

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, ie 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))

OpenCV uses internal codecs in imload function. But for cvtColor, it uses this formula:

RGB[A] to Gray:Y←0.299⋅R + 0.587⋅G + 0.114⋅B

It is a known behaviour (but looks like a bug :) ). You can track the history of it here and here .

COLOR_BGR2GRAY , as proposed in another answer will not work:

In [6]: gray1 = cv2.imread('1.png', 0)

In [7]: col = cv2.imread('1.png')

In [8]: gray2 = cv2.cvtColor(col, cv2.COLOR_RGB2GRAY)

In [10]: np.array_equal(gray1, gray2)
Out[10]: False

In [16]: gray3 = cv2.cvtColor(col, cv2.COLOR_BGR2GRAY)

In [17]: np.array_equal(gray1, gray3)
Out[17]: False

TLDR: They are different and it is OK. Just live with it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM