Converting image to grayscale - python

I want to convert any image to grayscale, but I don't understand the difference between these implementations.
image = cv2.imread('lenna.jpg')
gray = cv2.cvtColor(image, cv2.IMREAD_GRAYSCALE)
gray1 = rgb2gray(image)
gray2 = cv2.imread('lenna.jpg', cv2.IMREAD_GRAYSCALE)
image1 = Image.open('lenna.jpg', 'r')
gray3 = image1.convert('L')
When I plot them, I get them in blue scale, green scale, green scale and gray respectively. When I should use each one?

You've encountered a spot where Python's type system isn't protecting you in the way that C++ would.
cv2.IMREAD_GRAYSCALE and cv2.COLOR_BGR2GRAY are values from different enumerations. The former, whose numerical value is 0, applies to cv2.imread(). The latter, whose numerical value is 6, applies to cv2.cvtColor(). C++ would have told you that cv2.IMREAD_GRAYSCALE can't be passed to cv2.cvtColor(). Python quietly accepts the corresponding int value.
Thus, you think you're asking cv2 to convert a color image to gray, but by passing cv2.IMREAD_GRAYSCALE, cv2.cvtColor() sees the value 0, and thinks you're passing cv2.COLOR_BGR2BGRA. Instead of a grayscale image, you get the original image with an alpha channel added.
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
is what you need instead.
The other issue you're seeing, assuming you're using a Jupyter notebook, is that cv2 layers color planes in BGR order instead of RGB. To display them properly, first do
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
and then display the result.

The images that are not gray are the still 3d arrays, that is to say they still somehow retain color information, the reason you are seeing blue and green is because in those 3d arrays the red and green channels in the first case and the blue & red channels in the second have been reduced to 0 leaving only the blue and green that you see.
In order to read the image as grayscale you would use
img_gray=cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
This will yield a 2d array with values between 0 and 255 corresponding to how bright the pixel should be instead of how bright each of the 3 color channels of the pixel should be.

Related

How delete channel in image opencv

I i have two different images(frames of video):
first image:
print(img1.shape)
(31,27,3)
second image:
print(img2.shape)
(31,27)
How i can delete on first image the value 3?
img1.shape variable returns height, width, channel of your current image.
How i can delete on first image the value 3?
3 refers to BGR channel in your image.
(I assume you read the image using cv2.imread)
You can convert to the gray-scale by
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
Now print(img1.shape)
and result will be:
(31, 27)
The 3 means that you have an RGB image, a color image.
If you want to make it grayscale, as I guess your second image is, use
from skimage import color
gray_image = color.rgb2gray(image)

Correct way for converting RGB heatmap image to Grayscale heatmap

I am trying to convert a RGB heatmap image to grayscale heatmap image. First I thought It was a simple rgb to grayscale conversion. But it isn't.
For example, blue color may represent soft things and red color may represent hard things.
Using commonly used simple rgb to grayscale conversion method, I found red and blue color has converted to save gray color although they had very different nature of representation.
But What I want something like this where blue is deep gray, and red is bright gray.
I had searched a lot. Unfortunately I did't find (or maybe I couldn't understand). After reading some article on rgb color model, I have found a way to generate grayscale image. My code is
import colorsys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img = mpimg.imread('input_image/abnormal_hi_res.png')
img = img[ : , : , 0:3] # keep only r, g, b channels
new_image = np.zeros((img.shape[0], img.shape[1]))
for y_pos in range(img.shape[0]):
for x_pos in range (img.shape[1]):
color = img[y_pos, x_pos]
r,g,b = color
h, _, _ = colorsys.rgb_to_hls(r, g, b)
new_image[y_pos, x_pos] = 1.0 - h
plt.imshow(new_image, cmap='gray')
plt.show()
But I believe there should exists a good method backed by proven mathematics.
Please help me to find out the correct one for this problem.
You can follow these links. They have got some good notes on heatmaps and grayscale
https://docs.opencv.org/3.1.0/de/d25/imgproc_color_conversions.html
https://matplotlib.org/users/colormaps.html
*UPDATE
First, you need to convert your BGR image to LUV then convert it to a grayscale image. Use opencv.
Code for converting BGR to LUV in opencv.
gray = cv2.cvtColor(img, cv2.COLOR_BGR2LUV)
I think it what you are looking for

Get a info about coloured pixels at gray image. Python, opencv

I have small r g b image. Imake it gray.
original = cv2.imread('im/auto5.png')
print(original.shape) # 27,30,3
print(original[13,29]) # [254 254 254]
orig_gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
print(orig_gray.shape) # 27,30
Is it in this array info about white and black pixels? Or it lost this data? What mean this numbers?
print(orig_gray[5,5]) #6
At r g b image it mean color (3 digits, like [254,254,254]). But what mean one digit in my case with gray image? I want to get quanity of white pixels for my recognising.
Once you convert to gray scale there is only one value for each 'pixel' or index in the 2D array which represents the brightness in the original RGB image. The RGB image is essentially 3 of these arrays which represent the brightness for each of the three colors.
The idea of a 'white pixel' is a little confusing. I guess you could say any location in the grayscale array with a value of 255 is a white pixel. That would be an RGB pixel which is fully saturated at (255, 255, 255). There is basically only one value for each pixel after converting to gray scale.
Hope that helps.

opencv python copy mask region (black or white pixels) onto a BGR image region

In OpenCV python, say we read an image with cv2.imread and get a BGR numpy array. We next generate a mask with the cv2.inRange command. The mask has the same width/height and each mask pixel is either black or white.
I want to copy a region from the mask (taken as an image of black and white pixels) onto a region of the color image.
How do I do that? This does not work
img[10:20,10:20] = mask[10:20,10:20]
Must I convert the mask to BGR image first? If so how?
Edit: I do not want to apply the whole mask to the image as in apply mask to color image. Another way to say what I want: see the mask as a black and white image. I want to copy a region of that image (as a set of black or white pixels) onto another image. The resulting image will be a color image except for one smaller rectangular region that contains only black or white pixels. The result will be similar to if I in photoshop copy a rectangular area of a black/white image and past that rectangle onto an area of a color image.
(I'm new to OpenCV)
If you try to do it with a single channel (grayscale) mask directly, the shapes of the array slices will not be the same, and the operation will fail.
>>> img[10:20,10:20] = mask[10:20,10:20]
ValueError: could not broadcast input array from shape (10,10) into shape (10,10,3)
You have to convert the mask to BGR, which will make it 3 channels, like the original image.
>>> bgr_mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
>>> img[10:20,10:20] = bgr_mask[10:20,10:20]

What is a "twice HSV transformation"?

I learned this method from a SPIE Proceeding article, they used the twice HSV transformation for shadow detection. In their paper, the method was stated as following:
Firstly, the color model of the image is transformed from RGB to HSV,
and the three components of the HSV model are normalized to 0 to 255,
then the image is transformed from RGB to HSV once again. Thirdly, the
image is turned into a gray image from a color image, only the gray
value of the red component is used. Fourthly, the OTSU thresholding
method is used to produce a threshold by which the image is converted
to a binary image. Since the gray value of the shadow area is usually
smaller than those areas which are not covered by shadow, the
objective is pixels whose gray value is below the threshold, and
background is pixels whose gray value is beyond the threshold.
Do the second and third steps make sense?
The second and third statements absolutely don't make any sense whatsoever. Even the pipeline is rather suspicious. However, after re-reading that statement probably a dozen times, here is what I came up with. Apologies for any errors in understanding.
Let's start with the second point:
Firstly, the color model of the image is transformed from RGB to HSV, and the three components of the HSV model are normalized to 0 to 255, then the image is transformed from RGB to HSV once again
You're well aware that transforming an image from RGB to HSV results in another three channel output. Depending on which platform you're using, you'll either get 0-360 or 0-1 for the first channel or Hue, 0-100 or 0-255 for the second channel or Saturation, and 0-100 or 0-255 for the third channel or Value. Each channel may be unequal in magnitude when comparing with the other channels, and so these channels are normalized to the 0-255 range independently. Specifically, this means that the Hue, Saturation and Value components all get normalized so that they all span from 0-255.
Once we do this, we now have a HSV image where each channel ranges from 0-255. My guess is they call this new image a RGB image because the channels all span from 0-255, just like any 8-bit RGB image would. This also makes sense because when you're transforming an image from RGB to HSV, the dynamic range of the channels all span from 0-255, so my guess is that they normalize all of the channels in the first HSV result to make it suitable for the next step.
Once they normalize the channels after doing HSV conversion as per above, they do another HSV conversion on this new result. The reasons why they would do this a second time are beyond me and don't make any sense, but that's what I gathered from the above description, and that's what they probably mean by "twice HSV transformation" - To transform the original RGB image to HSV once, normalize that result so all channels span from 0-255, then re-apply the HSV conversion again to this intermediate result.
Let's go to the third point:
Thirdly, the image is turned into a gray image from a color image, only the gray value of the red component is used.
The output after you transform the HSV image a second time, the final result is simply taking the first channel which is inherently a grayscale image and is the "red" channel. Coincidentally, this also corresponds to the Hue after you do a HSV conversion. I'm not quite sure what properties the Hue channel holds after converting the image using HSV twice, but maybe it worked for this particular method.
I decided to give this a whirl and see if this really works. Here's an example image of a shadow I found online:
Source: http://petapixel.com/
The basic pipeline is to take an image, convert it into HSV, renormalize the image so that the values are 0-255 again, do another HSV conversion, then do an adaptive threshold via Otsu. We threshold below the optimal value to segment out the shadows.
I'm going to use OpenCV Python, as I don't have the C++ libraries set up on my computer here. In OpenCV, when converting an image to HSV, if the image is unsigned 8-bit RGB, the Saturation and Value components are automatically scaled to [0-255], but the Hue component is scaled to [0-179] in order to fit the Hue (which is originally [0-360)) into the data type. As such, I scaled each value by (255/179) so that the Hue gets normalized to [0-255]. Here's the code I wrote:
import numpy as np # Import relevant libraries
import cv2
# Read in image
img = cv2.imread('shadow.jpg')
# Convert to HSV
hsv1 = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Renormalize Hue channel to 0-255
hsv1[:,:,0] = ((255.0/179.0)*hsv1[:,:,0]).astype('uint8')
# Convert to HSV again
# Remember, channels are now RGB
hsv2 = cv2.cvtColor(hsv1, cv2.COLOR_RGB2HSV)
# Extract out the "red" channel
red = hsv2[:,:,0]
# Perform Otsu thresholding and INVERT the image
# Anything smaller than threshold is white, anything greater is black
_,out = cv2.threshold(red, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Show the image - shadow mask
cv2.imshow('Output', out)
cv2.waitKey(0)
cv2.destroyAllWindows()
This is the output I get:
Hmm.... well there are obviously some noisy pixels, but I guess it does work.... kinda!

Categories