Related
I work on image pixels. I want to colorize my image in a specific formula and convert and save the image into RGB space after working with HSV. Opencv has functions to convert color spaces, But the color of the image changes my image. and I use the function to convert the pixel pixel, but my loop has a lot of time to run.
I wrote a piece of code in Python that contains a loop that takes a lot of time to execute.
What operational solutions do you have to reduce execution time?
I used the thread, but it is not implemented correctly and the time does not decrease.
Also, I used the np.apply_along_axis, but, this function increased the run time!
for i in range(row):
for j in range(col):
final[i][j][0], final[i][j][1], final[i][j][2] = colorir.HSV(final[i][j][0], final[i][j][1], final[i][j][2], max_sva=255).rgb()
When I convert the color with this code, my image is displayed in the correct color that I want, but when I use the following function, the image coloring is completely wrong:
final = cv2.cvtColor(final,cv2.COLOR_HSV2RGB)
or
final = matplotlib.colors.hsv_to_rgb(final)
Another question that occurred to me is that Is there a way to save an image that is in color space HSV without converting it to color space RGB? Without coloring it wrong? So that I don't have to use this snippet of runtime code above to convert?
Edit:
My image size is variable: for example 600x600
The execution time of these loops is about 15 seconds, which should be approximately less than 1 second.
I color the gray level image.
Below is my executable code:
import numpy as np
from numpy import newaxis
import cv2
from colorir import HSV, sRGB
zero1 = np.zeros((row, col), dtype=int)
new1 = np.dstack((zero1, zero1, num12))
new1 = np.where(num12 > 250, 0, num12)
newww = np.where(new1 < 0, 0, new1)
minus = np.subtract(num1, num2)
minus_2 = minus
minus = np.where(minus <=0, 33, minus)
minus = np.where(num2 >= np.multiply(num1 , 1.1), 33, minus)
minus = np.where(np.logical_and(num2 <= np.multiply(num1 , 1.1),num2 >= np.multiply(num1 , 1)), 107, minus)
minus = np.where(num2 < np.multiply(num1 , 1), 209, minus)
a_255 = np.full([row, col], 255, dtype=int)
final = np.dstack((minus, newww, a_255))
for i in range(row):
for j in range(col):
final[i][j][0], final[i][j][1], final[i][j][2] = HSV(final[i][j][0], final[i][j][1], final[i][j][2], max_sva=255).rgb()
My final image should only contain the green, blue, and orange colors I specified, but the image colored by functions 1 is pink and yellow, and unrelated colors.
A small example:
final = [[[105, 213, 235], [105, 213, 235], [105, 213, 235], [105, 213, 235], [105, 213, 235], [105, 213, 235]]]
final = np.asarray(final)
final = cv2.cvtColor(final,cv2.COLOR_HSV2BGR)
cv2.imshow("image", final)
cv2.waitKey(0)
When I run above sample code, with cv2.cvtColor(final,cv2.COLOR_HSV2BGR), I encounter:
error (Unsupported depth of input image: 'VDepth::contains(depth)' where 'depth' is 4 (CV_32S))
I have to use np.float32, but np.float32 color will spoil the final image.
There are two issues here:
OpenCV uses a range of 0-180 for the hue channel (because 360 doesn't fit in an 8-bit integer).
OpenCV defaults to BGR, but if you want RGB, you need to use cv2.COLOR_HSV2RGB, not cv2.COLOR_HSV2BGR.
Here is my version of your example at the end:
import numpy as np
import cv2
from colorir import HSV, sRGB
hsv = [[[105, 213, 235], [105, 213, 235], [105, 213, 235]]]
hsv = np.asarray(hsv)
rgb_colorir = np.zeros_like(hsv)
for i in range(hsv.shape[0]):
for j in range(hsv.shape[1]):
rgb_colorir[i][j][0], rgb_colorir[i][j][1], rgb_colorir[i][j][2] = HSV(hsv[i][j][0], hsv[i][j][1], hsv[i][j][2], max_sva=255).rgb()
hsv[:,:,0] //= 2 # OpenCV's different definition
hsv = hsv.astype(np.uint8) # OpenCV requires uint8
rgb_opencv = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
Examining the converted images:
>>> rgb_colorir
array([[[ 88, 235, 39],
[ 88, 235, 39],
[ 88, 235, 39]]])
>>> rgb_opencv
array([[[ 91, 235, 39],
[ 91, 235, 39],
[ 91, 235, 39]]], dtype=uint8)
So we can see that there's a small difference in the red channel, which is likely caused by rounding errors. For the OpenCV case we had to round the hue to the nearest even number of degrees (because the integer divide by 2). It should be fairly difficult to see this difference.
If you want to use the more precise conversion, note that colorir.HSV is just calling colorsys.hsv_to_rgb, and doing a whole lot of extra work like building an object. But it doesn't give you anything extra except a normalization. Using colorsys.hsv_to_rgb directly should be faster. Also, colorsys is part of the standard library, using it directly should be preferred.
Use np.apply_along_axis() to iterate over your image. This should be relatively fast, but it will not be as fast as the OpenCV solution.
import colorsys
rgb_colorsys = np.apply_along_axis(
lambda hsv: colorsys.hsv_to_rgb(hsv[0] / 360, hsv[1] / 255, hsv[2] / 255),
2,
hsv,
) * 255
The result:
>>> rgb_colorsys
array([[[ 87.77941176, 235. , 38.70588235],
[ 87.77941176, 235. , 38.70588235],
[ 87.77941176, 235. , 38.70588235]]])
Of course there are other libraries you could try, such as scikit-image:
import skimage.color
rgb_skimage = skimage.color.hsv2rgb(hsv / [360, 255, 255]) * 255
Scikit-image also has a different definition for how it stores the HSV values. It also works in floating-point format, to avoid rounding errors. Result:
>>> rgb_skimage
array([[[ 87.77941176, 235. , 38.70588235],
[ 87.77941176, 235. , 38.70588235],
[ 87.77941176, 235. , 38.70588235]]])
With DIPlib (disclosure: I'm an author):
import diplib as dip
hsv_dip = dip.Convert(dip.Image(hsv, tensor_axis=2), "SFLOAT")
hsv_dip.SetColorSpace("HSV")
rgb_dip = dip.ColorSpaceManager.Convert(hsv_dip / [1, 255, 1], "RGB")
Result:
>>> np.asarray(rgb_dip)
array([[[ 87.77941176, 235. , 38.70588235],
[ 87.77941176, 235. , 38.70588235],
[ 87.77941176, 235. , 38.70588235]]])
Notice how OpenCV is the only implementation that has significant rounding errors, the others produce identical values.
For the floating-point results, np.round(...).astype(np.uint8) will get you an RGB image that you can display normally in pyplot:
>>> np.round(rgb_dip).astype(np.uint8)
array([[[ 88, 235, 39],
[ 88, 235, 39],
[ 88, 235, 39]]], dtype=uint8)
I have a 2D python list named image, the array contain only integer in the range from 0-255.
For example, the list is like this
image = [[0, 39, 57], [255, 182, 124], [19, 223, 200], [176, 190, 100]]
I have read How to convert a python numpy array to an RGB image with Opencv 2.4?, so i converted the python list to numpy array
image = np.asarray(image)
I could run the following
cv2.imwrite('image.jpg', image)
But how do i load the array as image directly without saving it to file ? something like this
opencvImage = cv2.imread(image, 0)
The above code obviously don't work, so how do i load python list or numpy array directly to opencv without saving it as file? the expected image output is rgb image or greyscale image, and the expected shape is the shape of the list/array itself.
The following code also don't work, i also don't understand why i can save the numpy array as image file but opencv cannot show it directly
cv2.imshow("image", image)
The code above throws assertionfailed error from opencv
Based on the provided information, the only possible output is a gray image with the shape (4, 3). OpenCV image are just basic Numpy arrays. You can convert the list using:
lst = [[0, 39, 57], [255, 182, 124], [19, 223, 200], [176, 190, 100]]
image = np.asarray(lst, dtype=np.uint8) # Specifying dtype is critical here
# image = array([[ 0, 39, 57],
# [255, 182, 124],
# [ 19, 223, 200],
# [176, 190, 100]])
Then you can show it with cv2.imshow("image", image) but be aware that the image is a bit too small to be easily seen. Note also that a cv2.waitKey() loop may be needed so the window does not freeze and print the image correctly.
You can crease numpy.array then divide by 255.0 then show the image with cv2 like below:
import numpy as np
import cv2
image = [[0, 39, 57], [255, 182, 124], [19, 223, 200], [176, 190, 100]]
image = np.array(image, copy=False) / 255.0
cv2.imshow("image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Or you can use matplotlib for showing the image like below:
import matplotlib.pyplot as plt
import numpy as np
image = [[0, 39, 57], [255, 182, 124], [19, 223, 200], [176, 190, 100]]
image = np.array(image, copy=False) / 255.0
plt.imshow(image); plt.axis('off')
plt.show()
Output:
Is it possible to convert an RGBA image to BGRA without using the following code, i.e. without using opencv?
image = self.cv2.cvtColor(image, self.cv2.COLOR_RGBA2BGRA)
You can convert an image from BGRA to RGBA with this line of code:
image[..., :3] = image[..., 2::-1]
Of course, it modifies the existing array rather than creating a new one (which is good if you don't plan on using the old one again as it's more efficient). Another way is image = image[..., [2,1,0,3]], but since it uses fancy indexing, rather than modifying the old array, it creates a copy of the old array which takes up more memory.
Using the same line of code again converts the image back from RGBA to BGRA.
Like this with "fancy indexing":
# Make a dummy, random 4-channel image
RGBA = np.random.randint(0,256,(2,3,4), np.uint8)
In [3]: RGBA
Out[3]:
array([[[102, 204, 36, 128],
[178, 151, 166, 45],
[199, 49, 104, 98]],
[[ 79, 33, 223, 62],
[ 26, 34, 233, 254],
[ 62, 20, 57, 149]]], dtype=uint8)
# Convert RGBA to BGRA
BGRA = RGBA[..., [2,1,0,3]]
In [5]: BGRA
Out[5]:
array([[[ 36, 204, 102, 128],
[166, 151, 178, 45],
[104, 49, 199, 98]],
[[223, 33, 79, 62],
[233, 34, 26, 254],
[ 57, 20, 62, 149]]], dtype=uint8)
I think OpenCV likes its data contiguous, so if you get issues, use:
BGRA = RGBA[..., [2,1,0,3]].copy()
I want to import some images with its split RGB values, For some images it works and for some others, the output gives just one value for RGB of a pixel.
Here's the image for which the code works:
if os.path.isfile(location1):
image = imageio.imread(location1)
print("Type : ", type(image[0][0]))
## Type : imageio.core.util.Image
input : image
output: Image([[[167, 126, 94],
[210, 184, 147],
[245, 234, 188],
...,
And this is the image for which the code doesn't work.
if os.path.isfile(location2):
image = imageio.imread(location2)
print("TYpe : ", type(image[0][0]))
## TYpe : <class 'numpy.uint8'>
input: image
output: Image([[81, 78, 74, ..., 72, 71, 69],
[74, 71, 67, ..., 70, 70, 68],
[61, 58, 55, ..., 65, 65, 64],
...,
(I would appreciate any help)
It seems to be the second image you loaded is simply a grayscale image (i.e. not an image with color, but only with gray levels). To convert it to RGB, try the following:
from skimage import color
img = color.gray2rgb(your_image)
Also, as the conversion to RGB is just to repeat each gray value three times, you can use this snippet
import numpy as np
rgb = np.stack((your_image, your_image, your_image), axis=-1)
In numpy, The array method could convert a image to a big array, the question is , whats' the meaning of the numbers in this array?(RGB value? gray-scale value? )
What's more, when I convert a image to mode"1", tried
im = Image.open("test.jpg")
# Some processing ...
im = im.convert("1")
im_arr = array(im,dtype=uint8)
I found that the im_arr array shows not only 0 and 255:
array([[170, 170, 170, ..., 255, 255, 255],
[255, 248, 255, ..., 175, 255, 222],
[255, 255, 247, ..., 175, 170, 171],
...,
[ 32, 105, 110, ..., 32, 124, 32],
[ 32, 32, 32, ..., 101, 115, 39],
[ 41, 10, 32, ..., 109, 111, 115]], dtype=uint8)
Why? (I really want to know the meaning of numbers in array of different modes)
By running im.convert("1"), you are converting your loaded image into a bilevel image which should only have pixel values of 0 or 255. To do this properly,
im_arr = np.array(im.getdata(), dtype = np.uint8).reshape(im.size[0], im.size[1])
With this, you will receive your desired bilevel image, which you could save or view with matplotlib.pyplot.imshow.
The odd number you are receiving are coming from the conversion process to a numpy array. If you choose different data types (np.int, np.int8, np.uint8, ...), you will find that each of these return a different array.
Without knowing much about the object structure of PIL.Image, I couldn't say why this is. But I think it's safe to say that without .getdata() the results are garbage.
The format jpeg does not support mode 1. Save it into a different format, png or bmp, and look at the array for that one instead.
Documentation for PIL Jpeg