Reputation: 23
Im working on filtering out certain colors of images, but am stuck when it comes to working with the hsv/hsl colorspaces. If i try to convert a Image read with matplotlib to HSV using cv2.Color(image, cv2.COLOR_RGB2HSV)
the resulting image has messed up color just like this:
In my understanding, the Image should be displayed just like the original
.
Is this correct or does the mistake lie within my limited understanding of color spaces?
I have tried also reading the image using opencv and then converting for BGR to HSV, which brought different, but still defect images. Also i converted the image to uint8 using image = (image * 255).round().astype(np.uint8)
, still did not work
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import cv2
import numpy as np
image = mpimg.imread("Screenshots/vlcsnap-2019-07-22-08h42m14s251.png")
hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
hls = cv2.cvtColor(image, cv2.COLOR_RGB2HLS)
plt.figure()
plt.imshow(hls)
plt.figure()
plt.imshow(hsv)
Besides im also getting the following warnign when running the code: "Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers)."
Upvotes: 2
Views: 7531
Reputation: 2086
There's in important consequence of the Hue (H) channel being a continuous color circle that you should keep in mind when visualizing HSV conversions. That kind of blockiness is unavoidable, even when you fix the clipping due to not rescaling the 0-360 H channel to 0.0-1.0 for plotting as a float32 with matplotlib.
Unlike the linear scale of the Saturation (S) and Value (V) channels, the min and max values of the H channel are actually adjacent values of red on the color circle. Due to sharp transitions between 0 and either 255 or 1.0 in what is actually similarly redish parts of the original image, you'll often see harsh, blocky areas in whatever channel represents H.
You haven't actually posted your original image, but what looks like a screenshot from a matplotlib plot, so I can't reproduce your effect exactly. However, to see the effect, look at just the H channel by itself with your original and I think you'll see what I mean:
H, S, V = cv2.split(hsv)
# here's the opencv way to see it
cv2.imshow('H', H)
cv2.waitKey(0)
Notice the blocky transitions where very light and very dark areas are right next to each other, even though there are smooth red variations there in the original image.
You're using matplotlib, which expects RGB images. So Hue is shown in Red, Saturation in green, and Value in Blue. Everywhere you're seeing those non-red blocks, it's where Hue is close to min 0.0, and everywhere you're seeing normal (for HSV) orange/yellow/white/etc, it's where Hue is closer to max 1.0. The clipping due to not normalizing the 0-360 H channel to 0.0-1.0 exaggerates the problem, but it'll still be there after fixing that.
Upvotes: 4
Reputation: 11420
By the documentation of imshow
:
(M, N): an image with scalar data. The data is visualized using a colormap.
(M, N, 3): an image with RGB values (0-1 float or 0-255 int).
(M, N, 4): an image with RGBA values (0-1 float or 0-255 int), i.e. including transparency.
This already tells you that whatever matrix of 3 channels you give it (like HSV), it will taken as RGB image. That means, that whatever number is in H it will be taken as R and so on. So Hue values will be the red ones, Saturation values the green ones and value ones will be blue.
The warning you mentioned call my attention and I decided to test it, since I normally do not use matplotlib
to load/show images. First thing I notice is that the images are type float32
and are from 0-1 when I used:
image = mpimg.imread("imagePath.png")
This is ok, and it is the same for OpenCV float32 images.
Now, if you read the documentation of cvtColor and check the color conversion you will see that when the image is type float32 the ranges are:
H :[0-360]
S :[0-1.]
V :[0-1.]
So this means that most of the values of the first channel of the image will be above 1. and then it will go over the range for float for the function imshow (0-1) which will clip it and then be 1.
As I said before, this image will be interpreted as RGB; therefore, the Hue channel will be the Red channel, and most of the values are max so most of your image will be a tone of red. The green areas are due to big saturation (close to max). However since it is where it is like white, I would say you are displaying HLS and it is the L channel at max.
Now it comes to the question, what are you expecting to be the result? HSV color space will always show you quite a different thing from the original, everything depends on the colors, but small changes such as more or less saturated will give you greener images....
In any case, if you want to show something more reasonable, divide the first channel by 360 which will scale it to be from 0-1. as the other 2 channels. However the green parts won't dissappear but it will look less red.
Upvotes: 3