I am using OpenCV to read and display an image. I am trying to do a scalar multiplication but it is being displayed very differently for two similar approaches:
img = cv2.imread('C:/Python27/user_scripts/images/g1.jpg', -1)
cv2.imshow('img_scaled1', 0.5*img)
cv2.waitKey(0)
cv2.imshow('img_scaled2', img/2)
cv2.waitKey(0)
In the 1st case, hardly anything is displayed. 2nd case works fine.
It seems to me that imshow() does not support numpy array of floats.
I want to use the first method. Can somebody help?
There are lot of pitfall when using images. This one seems like a type issue.
imshowaccept uint8 arrays in range(0,256) (256 excluded), and float arrays in range(0.0,1.0). When doing a=a*.5, you have a float array out of range, so no warranty on the result.
A solution is to cast the array in the uint8 type by:
imshow((a*.5).astype(np.uint8))
or
imshow((a*.5).astype('uint8'))
Related
Bellow is the following mask showing the detected object by using histogram back projection
The image has the type float32 which results from the algorithm's output. I want to detect contours using cv2.findContours function.
As you know this function accept a certain image type which is uint8, otherwise it raises ans error. Therefore, I converted the image type from float32 into uint8 using imageFloat.astype(np.uint8).
When displaying the new converted binary image (new uint8) it displays a black image which means that the detected object is no longer visible (Zero mask)
So my question is: anyone know why this happens and what i'm doing wrong?
Thanks in advance
Khaled
You are not scaling up the values of the image pixels before converting to int, this is the reason why you are facing error.
Do this:
imageFloat *= 255
imageFloat.astype(np.uint8)
I'm currently trying to start with an original RGB image, convert it to LUV, perform some operations (namely, rotate the hues), then rotate it back to RGB for display purposes. However, I'm encountering a vexing issue where the RGB-to-LUV conversion (and vice versa) seems to be changing the image. Specifically, if I begin with an LUV image, convert it to RGB, and then change it back to LUV, without changing anything else, the original image is different. This has happened for both the Python (cv2) and Matlab (open source) implementations of the color conversion algorithms, as well as my own hand-coded ones based on. Here is an example:
luv1 = np.array([[[100,6.12,0]]]).astype('float32')
rgb1 = cv2.cvtColor(luv1,cv2.COLOR_Luv2RGB)
luv2 = cv2.cvtColor(rgb1,cv2.COLOR_RGB2Luv)
print(luv2)
[[[99.36293 1.3064307 -1.0494182]]]
As you can see, the LUV coordinates have changed from the input. Is this because certain LUV coordinates have no direct match in RGB space?
Yes, remove the astype('uint8') bit in your code, and the difference should disappear if the conversion is implemented correctly.
You can see the equations for the conversion in Wikipedia. There is nothing there that is irreversible, the conversions are perfect inverses of each other.
However, this conversion contains a 3rd power, which does stretch some values significantly. The rounding of the conversion to an integer can introduce a significant shift of color.
Also, the Luv domain is highly irregular and it might not be easy to verify that Luv values will lead to a valued RGB value. Your statement "I've verified that luv1 has entries that all fall in the allowable input ranges" makes me believe that you think the Luv domain is a box. It is not. The ranges for u and v change with L. One good exercise is to start with a sampling of the RGB cube, and map those to Luv, then plot those points to see the shape of the Luv domain. Wikipedia has an example of what this could look like for the sRGB gamut.
The OpenCV cvtColor function will clamp RGB values to the [0,1] range (if of type float32), leading to irreversible changes of color if the input is out of gamut.
Here is an example that shows that the conversion is reversible. I start with RGB values because these are easy to verify as valid:
import numpy as np
import cv2
rgb1 = np.array([[[1.0,1.0,1.0],[0.5,1.0,0.5],[0.0,0.5,0.5],[0.0,0.0,0.0]]], 'float32')
luv1 = cv2.cvtColor(rgb1, cv2.COLOR_RGB2Luv)
rgb2 = cv2.cvtColor(luv1, cv2.COLOR_Luv2RGB)
np.max(np.abs(rgb2-rgb1))
This returns 2.8897537e-06, which is numerical precision for 32-bit floats.
In OpenCV (Python), to convert RGB to YCbCr we use:
imgYCC = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)
What if i want to come back to RGB?
Check the docs for color conversions. You can see all of the available color conversion codes here: Conversion Color Codes.
For the colorspaces available, you can generally transform both ways---COLOR_BGR2YCrCb (i.e. BGR-to-YCrCb) and COLOR_YCrCb2BGR (i.e. YCrCb-to-BGR). Also, OpenCV uses BGR ordering, not RGB ordering. Regardless, to answer the specific question at hand, simply convert back using the opposite order of the colorspaces:
img_bgr = cv2.cvtColor(imgYCC, cv2.COLOR_YCrCb2BGR)
Note: cv2.COLOR_YCrCb2BGR is equivalent to cv2.COLOR_YCR_CB2BGR, I just find the first variant easier to read. Since these transformations (on uint8 images especially), there's some rounding going on so you won't necessarily get the exact same image going back and fourth. But you shouldn't be more than like 1 off at a few of the locations.
I have to translate a code from Octave to Python, among many things the program does something like this:
load_image = imread('image.bmp')
which as you can see its a bitmap, then if I do
size(load_image) that prints (1200,1600,3) which its ok, but, when I do:
load_image
it prints a one dimensional array, that does not make any sense to me, my question is how in Octave are these values interpreted because I have to load the same image in opencv and I couldn't find the way.
thanks.
What you have is a 3D array in octave. Here in the x-dimension you seem to have RGB values for each pixel and Y and Z dimension are the rows and columns respectively. However when you print it you will see all the values in the array and hence it looks like a 1D array.
Try something like this and look at the output:
load_image(:,:,i)
The i stands for the dimensions of your image RGB. If you want to 2D print your 3D image using matplotlib or similar, you need to do the same.
Edit: original title "convert numpy array to cvmat" was a mistake - OpenCV's less than useful error message and my not reading the docs!
With OpenCV 2, IPython now uses NumPy arrays by default.
cvimage = cv2.imread("image.png") #using OpenCV 2
type(cvimage)
Out: numpy.ndarray #dtype is uint8
pltimage = plt.imread("image.png") #using Matplotlib
type(pltimage)
Out: numpy.ndarray #dtype is float
plt.imshow(cvimage) # works great
cv2.imshow(cvimage)
TypeError: Required argument 'mat' (pos 2) not found
Since cv2 uses NumPy arrays by default, there is no longer any cv::Mat constructor and NumPy has no functions to convert to a cv::Mat array.
Any ideas?
The function has the following docstring: imshow(winname, mat) -> None.
You can see the doc string by typing cv2.imshow.__doc__ in the interpreter.
Try cv2.imshow('Image', cvimage).
tl;dr : In original question, first argument of "window name" was missing. "imshow" takes two parameters and only one was supplied.
The question technically asks how to convert a NumPy Array (analogous to CV2 array) into a Mat object (CV). For anyone who is interested, this can be done by:
mat_array = cv.fromarray(numpy_array)
where mat_array is a Mat object, and numpy_array is a NumPy array or image.
I would suggest staying away from older CV structures where possible. Numpy arrays offer much better performance than implemenations in native Python
Mat object was needed because C/C++ lacked a standard/native implementation of matrices.
However, numpy's array is a perfect replacement for that functionality. Hence, the cv2 module accepts numpy.arrays wherever a matrix is indicated in the docs.