Understand How Color to Gray Scale Works Using OpenCV

From color to gray scale

The first thing to understand is that when we convert a color image to a gray scale image it will lose information. That means, you cannot convert a color image to gray scale and back to a color image without losing quality.

import cv2

img = cv2.imread("image.jpeg")
img = cv2.resize(img, (200, 300))
cv2.imshow("Original", img)

# OpenCV can convert it to gray scale for you
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("Gray", gray)

# And convert it back to color
color = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
cv2.imshow("Color", color)

Resulting in the following output.

Output

And as you see, the conversion back to color is only adding the gray scale value to the 3 channels of colors RGB.

Why do we lose information?

The key to understand is that a color image has three channels for each pixel, while a gray scale image only has one channel.

See the following illustration.

Describing the frame

As the above shows a gray scale frame only contains one number for each pixel, and the color image contains 3 numbers.

So how does the conversion happen?

How OpenCV converts to gray scale image

If you look in the documentation of cvtColor(…) you can find the conversion calculations.


Hence, we can make the same calculations.

import cv2
import numpy as np


img = cv2.imread("image.jpeg")
img = cv2.resize(img, (200, 300))
cv2.imshow("Original", img)

# The channels are BGR, hence the order is opposite
gray = img[:, :, 2]*0.299 + img[:, :, 1]*0.587 + img[:, :, 0]*0.114
gray = gray.astype(np.uint8)
cv2.imshow("Gray", gray)

cvt_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("CVT_Gray", cvt_gray)

Resulting in the following output.

Output

Which seem to quite close.

The conversion is no unique

Looking at wikipedia, there are other common ways to convert to gray scale.

Let’s try and see if we can see the difference.

import cv2
import numpy as np


img = cv2.imread("image.jpeg")
img = cv2.resize(img, (200, 300))

gray = img[:, :, 2]*0.299 + img[:, :, 1]*0.587 + img[:, :, 0]*0.114
gray = gray.astype(np.uint8)
cv2.imshow("Gray", gray)

gray = img[:, :, 2]*0.2126 + img[:, :, 1]*0.7152 + img[:, :, 0]*0.0722
gray = gray.astype(np.uint8)
cv2.imshow("Gray HDTV", gray)

gray = img[:, :, 2]*0.2627 + img[:, :, 1]*0.6780 + img[:, :, 0]*0.0593
gray = gray.astype(np.uint8)
cv2.imshow("Gray HDR", gray)

Resulting in the following.

I think you need higher resolution to really appreciate the difference.

Convert the color channels directly to gray scale

If you look at the conversions, they all favor green as the main value. Let’s see if we can see the difference if we only use the channels to convert to gray scale.

import cv2
import numpy as np


img = cv2.imread("image.jpeg")
img = cv2.resize(img, (200, 300))

gray = img[:, :, 2]
gray = gray.astype(np.uint8)
cv2.imshow("Red", gray)

gray = img[:, :, 1]
gray = gray.astype(np.uint8)
cv2.imshow("Green", gray)

gray = img[:, :, 0]
gray = gray.astype(np.uint8)
cv2.imshow("Blue", gray)

The result.

Where it is more easy to see the difference.

Leave a Reply