The color to grayscale algorithm is stated in the cvtColor() documentation. (search for RGB2GRAY). The formula used is the same as for CCIR 601:

Y = 0.299 R + 0.587 G + 0.114 B

The luminosity formula you gave is for ITU-R Recommendation BT. 709. If you want that you can specify CV_RGB2XYZ (e.g.) in the third parameter to cvtColor() then extract the Y channel.

You can get OpenCV to to do the "lightness" method you described by doing a CV_RGB2HLS conversion then extract the L channel. I don't think that OpenCV has a conversion for the "average" method, but if you explore the documentation you will see that there are a few other possibilities.

Answer from Bull on Stack Overflow
🌐
GeeksforGeeks
geeksforgeeks.org › python › python-grayscaling-of-images-using-opencv
Python | Grayscaling of Images using OpenCV - GeeksforGeeks
September 23, 2025 - Weighted formula: Gray = 0.2989*R + 0.5870*G + 0.1140*B (indices: R=[2], G=[1], B=[0] in OpenCV). Assigning [gray, gray, gray] preserves a 3-channel image for display. This produces visually more accurate grayscale than simple averaging, but ...
🌐
MathWorks
mathworks.com › computer vision toolbox
Convert RGB Image to Grayscale Image by Using OpenCV Importer - MATLAB & Simulink
The converter converts an RGB image to a grayscale image by eliminating the hue and saturation information while retaining the luminance. First import an OpenCV function into Simulink® by using the Install and Use Computer Vision Toolbox Interface for OpenCV in Simulink.
Discussions

How does one convert a grayscale image to RGB in OpenCV (Python)? - Stack Overflow
I'm learning image processing using OpenCV for a realtime application. I did some thresholding on an image and want to label the contours in green, but they aren't showing up in green because my im... More on stackoverflow.com
🌐 stackoverflow.com
Convert RGB into Grayscale - C++ - OpenCV
Hello! I am opening images from a folder using glob function. The images are RGB. I am trying to convert them to Grayscale using the cv::cvColor(RGB, GRAY, cv::COLOR_RGB2GRAY). For some reason memory problem appears in Visual Studio 2019 as shown in image above. Could someone help me ? More on forum.opencv.org
🌐 forum.opencv.org
0
June 29, 2021
python - Opencv - Grayscale mode Vs gray color conversion - Stack Overflow
For a format such as JPEG, which ... the RGB values and then converting those back to intensity). Reading directly as grayscale has the possibility of giving different results if the image file gets converted to a different format, as then a different grayscale conversion will be used. Say, you convert your JPEG file to PNG, then using IMREAD_GRAYSCALE will use different libraries to do the grayscale conversion, using OpenCV’s conversion ... More on stackoverflow.com
🌐 stackoverflow.com
image - "Standard" RGB to Grayscale Conversion - Stack Overflow
At the beginning, I used this formula ... used by OpenCV's CV_RGB2GRAY conversion): ... I wrote a simple code to test my results: it takes a color image and its PGM version (already converted using GIMP). Then it converts the color image using the previous formula. The goal is to have a grayscale image that ... More on stackoverflow.com
🌐 stackoverflow.com
🌐
Lindevs
lindevs.com › convert-rgb-image-to-grayscale-image-using-opencv
Convert RGB Image to Grayscale Image using OpenCV | Lindevs
April 2, 2023 - OpenCV uses weighted method, also known as luminosity method, for RGB to grayscale conversion. The grayscale value is calculated as the weighted sum of the R, G, and B color components. The formula is:
Top answer
1 of 5
216

I am promoting my comment to an answer:

The easy way is:

You could draw in the original 'frame' itself instead of using gray image.

The hard way (method you were trying to implement):

backtorgb = cv2.cvtColor(gray,cv2.COLOR_GRAY2RGB)

is the correct syntax.

2 of 5
18

Alternatively, cv2.merge() can be used to turn a single channel binary mask layer into a three channel color image by merging the same layer together as the blue, green, and red layers of the new image. We pass in a list of the three color channel layers - all the same in this case - and the function returns a single image with those color channels. This effectively transforms a grayscale image of shape (height, width, 1) into (height, width, 3)

To address your problem

I did some thresholding on an image and want to label the contours in green, but they aren't showing up in green because my image is in black and white.

This is because you're trying to display three channels on a single channel image. To fix this, you can simply merge the three single channels

image = cv2.imread('image.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray_three = cv2.merge([gray,gray,gray])

Example

We create a color image with dimensions (200,200,3)

image = (np.random.standard_normal([200,200,3]) * 255).astype(np.uint8)

Next we convert it to grayscale and create another image using cv2.merge() with three gray channels

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray_three = cv2.merge([gray,gray,gray])

We now draw a filled contour onto the single channel grayscale image (left) with shape (200,200,1) and the three channel grayscale image with shape (200,200,3) (right). The left image showcases the problem you're experiencing since you're trying to display three channels on a single channel image. After merging the grayscale image into three channels, we can now apply color onto the image

contour = np.array([[10,10], [190, 10], [190, 80], [10, 80]])
cv2.fillPoly(gray, [contour], [36,255,12])
cv2.fillPoly(gray_three, [contour], [36,255,12])

Full code

import cv2
import numpy as np

# Create random color image
image = (np.random.standard_normal([200,200,3]) * 255).astype(np.uint8)

# Convert to grayscale (1 channel)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Merge channels to create color image (3 channels)
gray_three = cv2.merge([gray,gray,gray])

# Fill a contour on both the single channel and three channel image
contour = np.array([[10,10], [190, 10], [190, 80], [10, 80]])
cv2.fillPoly(gray, [contour], [36,255,12])
cv2.fillPoly(gray_three, [contour], [36,255,12])

cv2.imshow('image', image)
cv2.imshow('gray', gray)
cv2.imshow('gray_three', gray_three)
cv2.waitKey()
🌐
Python Geeks
pythongeeks.org › python geeks › learn opencv › color, grayscale and binary image conversion in opencv
Color, Grayscale and Binary Image Conversion in OpenCV - Python Geeks
September 19, 2022 - For an RGB image, we have to add the values of respective red, green and blue pixels and divide by 3. ... # Importing OpenCV import cv2 # Reading the image in grayscale mode by setting the flag as 0 img = cv2.imread(r'C:\Users\tushi\Downloa...
Find elsewhere
🌐
DataFlair
data-flair.training › blogs › image-conversion-using-opencv
Image Conversion using OpenCV - Colored, Grayscale and Binary - DataFlair
March 2, 2024 - The Weighted Averaging Method is a manual approach to converting a colour image to grayscale by calculating the grayscale intensity of each pixel through a weighted average of its RGB (Red, Green, Blue) color values.
🌐
OpenCV
forum.opencv.org › c++
Convert RGB into Grayscale - C++ - OpenCV
June 29, 2021 - Hello! I am opening images from a folder using glob function. The images are RGB. I am trying to convert them to Grayscale using the cv::cvColor(RGB, GRAY, cv::COLOR_RGB2GRAY). For some reason memory problem appears …
Top answer
1 of 2
90

To illustrate, I've opened up this same color JPEG image:

once using the conversion

img = cv2.imread(path)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

and another by loading it in gray scale mode

img_gray_mode = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

Like you've documented, the diff between the two images is not perfectly 0, I can see diff pixels in towards the left and the bottom

I've summed up the diff too to see

import numpy as np
np.sum(diff)
# I got 6143, on a 494 x 750 image

I tried all cv2.imread() modes

Among all the IMREAD_ modes for cv2.imread(), only IMREAD_COLOR and IMREAD_ANYCOLOR can be converted using COLOR_BGR2GRAY, and both of them gave me the same diff against the image opened in IMREAD_GRAYSCALE

The difference doesn't seem that big. My guess is comes from the differences in the numeric calculations in the two methods (loading grayscale vs conversion to grayscale)

Naturally what you want to avoid is fine tuning your code on a particular version of the image just to find out it was suboptimal for images coming from a different source.

In brief, let's not mix the versions and types in the processing pipeline.

So I'd keep the image sources homogenous, e.g. if you have capturing the image from a video camera in BGR, then I'd use BGR as the source, and do the BGR to grayscale conversion cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

Vice versa if my ultimate source is grayscale then I'd open the files and the video capture in gray scale cv2.imread(path, cv2.IMREAD_GRAYSCALE)

2 of 2
2

cv2.imread(path, 0) (or rather cv2.imread(path, cv2.IMREAD_GRAYSCALE)) asks the image file reading code to load the image as grayscale. For most file types, some 3rd party library is used to read them. If this library supports grayscale conversion, we’ll be using that library’s conversion routine.

Otherwise, we’re using OpenCV’s implementation of the conversion to grayscale.

There’s no reason to assume one implementation is better than the other, the differences observed are likely due to a different computation order, or to a different assumption about the whitepoint. But note that if the file has a color profile embedded, the 3rd party library might be able to use it to do the conversion, and so will have whitepoint information available. This color profile does not get loaded into OpenCV, so OpenCV will always make an assumption about the whitepoint.

Reading the image directly as grayscale is possibly a bit more efficient. If the 3rd party library converts each pixel as it’s read, we won’t need a temporary memory space to store the full color image (which takes up 3x as much memory as the grayscale image). For a format such as JPEG, which stores intensity and color information separately, reading as grayscale also avoids a lot of computation (we’re directly outputting the intensity value, rather than computing the RGB values and then converting those back to intensity).

Reading directly as grayscale has the possibility of giving different results if the image file gets converted to a different format, as then a different grayscale conversion will be used. Say, you convert your JPEG file to PNG, then using IMREAD_GRAYSCALE will use different libraries to do the grayscale conversion, using OpenCV’s conversion code will ensure both files read identically.

🌐
Quora
quora.com › How-do-I-convert-a-color-image-to-gray-scale-in-OpenCV-with-Python
How to convert a color image to gray scale in OpenCV with Python - Quora
Answer: There are a couple ways of doing this, but this is a very basic way to convert. If you wish to have the HSV format and keep the 3 channels to modify hue/sat/brightness, then you will just need to change the 2nd parameter to the proper conversion. Below you will find my example of how to ...
🌐
GitHub
gist.github.com › abhinavjain241 › ffd77b3fffcb6359caa6
RGB to Grayscale Image - OpenCV · GitHub
RGB to Grayscale Image - OpenCV · Raw · rgb_to_grayscale.cpp · This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
🌐
Jeremy Morgan
jeremymorgan.com › tutorials › opencv › how-to-grayscale-image
Grayscaling Images Made Simple with OpenCV
We combine the three RGB channels into a single channel to convert an image to grayscale. To accomplish this, a weighted average is calculated taking into account each color’s sensitivity in the human eye. The formula is: ... Interesting right? That’s how we come up with grayscale images. OpenCV ...
🌐
Dev-akash
dev-akash.in › blogs › how-to-convert-color-image-to-grayscale-in-opencv
How to convert color image to grayscale in OpenCV
... So to convert the color image to grayscale we will be usingcv2.imread("image-name.png",0)or you can also writecv2.IMREAD_GRAYSCALEin the place of 0 as it also denotes the same constant.
🌐
TutorialsPoint
tutorialspoint.com › opencv › opencv_colored_images_to_grayscale.htm
OpenCV - Colored Images to GrayScale
Following is the syntax of this method. ... You can convert colored images to gray scale by passing the code Imgproc.COLOR_RGB2GRAY along with the source and destination matrices as a parameter to the cvtColor() method.
🌐
Mustafa Murat ARAT
mmuratarat.github.io › 2020-05-13 › rgb_to_grayscale_formulas
RGB to Grayscale Conversion | Mustafa Murat ARAT
May 13, 2020 - import cv2 import numpy as np import matplotlib.pyplot as plt %matplotlib inline img_path = 'img_grayscale_algorithms.jpg' img = cv2.imread(img_path) print(img.shape) #(1300, 1950, 3) #Matplotlib EXPECTS RGB (Red Greed Blue) #but... #OPENCV reads as Blue Green Red #we need to transform this in order that Matplotlib reads it correctly fix_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.imshow(fix_img) #Let's extract the three channels R, G, B = fix_img[:,:,0], fix_img[:,:,1],fix_img[:,:,2] This is the grayscale conversion algorithm that OpenCV’s cvtColor() use (see the documentation) The formula used is: \[Y = 0.299\times R + 0.587 \times G + 0.114 \times B\]
🌐
TutorialsPoint
tutorialspoint.com › converting-an-image-from-colour-to-grayscale-using-opencv
Converting an image from colour to grayscale using OpenCV
March 17, 2021 - Step 1: Import OpenCV. Step 2: Read the original image using imread(). Step 3: Convert to grayscale using cv2.cvtcolor() function.
Top answer
1 of 6
38

The problem is that I can't understand how the "official" JPG->PGM convertitors work in terms of what value to assign to the final pixel (i guess, 0->255) starting from the classic RGB format.

There is likely a gamma adjustment in the conversion those "official" tools are using.
That is, it is not just a linear transform.

See this Wikipedia section for the details: Converting color to grayscale

I believe you want to use the formula for Csrgb.
Try it out and see if it matches the results you're expecting.

Basically, you'll do this:

  1. Take R, G, B color (each in [0,1] range)
    • If they're in the range 0..255 instead, simply divide by 255.0
  2. Compute Clinear = 0.2126 R + 0.7152 G + 0.0722 B
    • This is likely the linear transform you were applying before
  3. Compute Csrgb according to it's formula, based on Clinear
    • This is the nonlinear gamma correction piece you were missing
    • Check out this WolframAlpha plot
    • Csrgb = 12.92 Clinear when Clinear <= 0.0031308
    • Csrgb = 1.055 Clinear1/2.4 - 0.055 when Clinear > 0.0031308
2 of 6
4

To harold's point about the "Y plane": standard color JPEGs are encoded using the YCbCr colorspace, where Y is the luminance component (i.e. the brightness) and Cb and Cr are the blue-difference and red-difference chroma components. So one way of turning a color JPEG into a grayscale one is to simply drop the Cb and Cr components.

There is a utility called jpegtran than can do this losslessly, using the -grayscale option. (The lossless part would really only matter if you wanted to end up with a JPEG and not PGM, to avoid generation loss.) In any case, this would probably be the fastest way to do this transformation, because it doesn't even decode the image into pixels, much less do math on each one.

🌐
Brandonrohrer
brandonrohrer.com › convert_rgb_to_grayscale.html
How to Convert an RGB Image to Grayscale
If close is good enough or if you really care about speed, use the linear approximation of gamma correction. This is the approach used by MATLAB, Pillow, and OpenCV. It is included in my Lodgepole image and video processing toolbox: import lodgepole.image_tools as lit gray_img = lit.rgb2gray_approx(color_img)