111

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 image is in black and white.

Early in the program I used gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) to convert from RGB to grayscale, but to go back I'm confused, and the function backtorgb = cv2.cvtColor(gray,cv2.CV_GRAY2RGB) is giving:

AttributeError: 'module' object has no attribute 'CV_GRAY2RGB'.

The code below does not appear to be drawing contours in green. Is this because it's a grayscale image? If so, can I convert the grayscale image back to RGB to visualize the contours in green?

import numpy as np
import cv2
import time

cap = cv2.VideoCapture(0)
while(cap.isOpened()):

    ret, frame = cap.read()

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    ret, gb = cv2.threshold(gray,128,255,cv2.THRESH_BINARY)

    gb = cv2.bitwise_not(gb)

    contour,hier = cv2.findContours(gb,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)

    for cnt in contour:
        cv2.drawContours(gb,[cnt],0,255,-1)
    gray = cv2.bitwise_not(gb)

    cv2.drawContours(gray,contour,-1,(0,255,0),3)

    cv2.imshow('test', gray)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
nathancy
  • 42,661
  • 14
  • 115
  • 137
user391339
  • 8,355
  • 13
  • 58
  • 71

5 Answers5

198

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.

Nicolas Gervais
  • 33,817
  • 13
  • 115
  • 143
Anoop K. Prabhu
  • 5,417
  • 2
  • 26
  • 43
  • 4
    I think this does not really work because you cannot convert back to RGB . You will get a 3 slot array with this but the RGB values will be same as the value it had in the grayscale image. – Megha Oct 25 '16 at 13:05
  • 12
    Yes. That's true. But the author of the question was not interested in using the original colors – Anoop K. Prabhu Oct 25 '16 at 13:09
  • 1
    But do you really see contours of green as the author wishes to see. My image still appears gray even after the coversion COLOR_GRAY2RGB. I think the end result will still be a gray image even though you will have RGB channels defined – Megha Oct 25 '16 at 13:15
  • 3
    Draw the contour after you do the color conversion – Anoop K. Prabhu Jan 05 '17 at 08:23
  • 1
    Note the image needs to have the correct datatype: `gray = np.array(gray, dtype=np.uint8)` – khaverim Apr 02 '21 at 19:37
  • Yes, OpenCV is picky and inconsistent about dtypes – qwr Mar 15 '22 at 05:43
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)

enter image description here

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

enter image description here enter image description here

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()
nathancy
  • 42,661
  • 14
  • 115
  • 137
13

Try this:

import cv2

color_img = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2RGB)
Tom Wojcik
  • 5,471
  • 4
  • 32
  • 44
delicasso
  • 187
  • 2
  • 6
12

Once you convert your image to gray-scale you cannot go back to original RGB image. You have gone from three channels to one channel, when you try to go back all three numbers will be the same. So the short answer is, no you cannot go back. The reason your backtorgb function this throwing that error is because it needs to be in the format:

CvtColor(input, output, CV_GRAY2BGR)

OpenCV use BGR not RGB, so if you fix the ordering it should work, though your image will still be gray.

tarmas99
  • 359
  • 1
  • 11
grromrell
  • 276
  • 1
  • 9
  • 1
    that actually didn't work. i've seen several references on the web indicating that the above is possible, but not in my hands. the autocomplete on my pydev ide lists CV_BGR2GRAY but not GRAY2BGR. puzzled. thanks! – user391339 Feb 06 '14 at 19:13
  • Indeed your answer is true and I faced the same problem – Megha Oct 25 '16 at 13:05
  • Using the right casing/module names it's certainly possible to convert from gray to BGR: cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) – Bjørn van Dommelen Jan 21 '22 at 09:39
1
rgb_image = cv2.cvtColor(binary_image, cv2.COLOR_GRAY2RGB) * 255

There can be a case when you think that your image is a gray-scale one, but in reality, it is a binary image. In such a case you have an array of 0's and 1's where 1 is white and 0 is black (for example).

In RGB space, pixel values are between 0 and 255. Therefore it is necessary to multiply by 255 your converted image. If not, you will receive an almost blank image, because pixels with value 0 are almost the same as the ones with value 1, when the values of pixels varies between <0, 255>

Adam Kuzański
  • 509
  • 4
  • 12