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
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)
I am trying to increase brightness of a grayscale image. cv2.imread() returns a numpy array. I am adding integer value to every element of the array. Theoretically, this would increase each of them. After that I would be able to put upper threshold of 255 and get the image with the higher brightness.
Here is the code:
grey = cv2.imread(path+file,0)
print type(grey)
print grey[0]
new = grey + value
print new[0]
res = np.hstack((grey, new))
cv2.imshow('image', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
However, numpy addition apparently does something like that:
new_array = old_array % 256
Every pixel intensity value higher than 255 becomes a remainder of dividing by 256.
As a result, I am getting dark instead of completely white.
Here is the output:
<type 'numpy.ndarray'>
[115 114 121 ..., 170 169 167]
[215 214 221 ..., 14 13 11]
And here is the image:
How can I switch off this remainder mechanism? Is there any better way to increase brightness in OpenCV?
One idea would be to check before adding value whether the addition would result in an overflow by checking the difference between 255 and the current pixel value and checking if it's within value. If it does, we won't add value, we would directly set those at 255, otherwise we would do the addition. Now, this decision making could be eased up with a mask creation and would be -
mask = (255 - grey) < value
Then, feed this mask/boolean array to np.where to let it choose between 255 and grey+value based on the mask.
Thus, finally we would have the implementation as -
grey_new = np.where((255 - grey) < value,255,grey+value)
Sample run
Let's use a small representative example to demonstrate the steps.
In [340]: grey
Out[340]:
array([[125, 212, 104, 180, 244],
[105, 26, 132, 145, 157],
[126, 230, 225, 204, 91],
[226, 181, 43, 122, 125]], dtype=uint8)
In [341]: value = 100
In [342]: grey + 100 # Bad results (e.g. look at (0,1))
Out[342]:
array([[225, 56, 204, 24, 88],
[205, 126, 232, 245, 1],
[226, 74, 69, 48, 191],
[ 70, 25, 143, 222, 225]], dtype=uint8)
In [343]: np.where((255 - grey) < 100,255,grey+value) # Expected results
Out[343]:
array([[225, 255, 204, 255, 255],
[205, 126, 232, 245, 255],
[226, 255, 255, 255, 191],
[255, 255, 143, 222, 225]], dtype=uint8)
Testing on sample image
Using the sample image posted in the question to give us arr and using value as 50, we would have -
Here is another alternative:
# convert data type
gray = gray.astype('float32')
# shift pixel intensity by a constant
intensity_shift = 50
gray += intensity_shift
# another option is to use a factor value > 1:
# gray *= factor_intensity
# clip pixel intensity to be in range [0, 255]
gray = np.clip(gray, 0, 255)
# change type back to 'uint8'
gray = gray.astype('uint8)
Briefly, you should add 50 to each value, find maxBrightness, then thisPixel = int(255 * thisPixel / maxBrightness)
You have to run a check for an overflow for each pixel. The method suggested by Divakar is straightforward and fast. You actually might want to increment (by 50 in your case) each value and then normalize it to 255. This would preserve details in bright areas of your image.
Use OpenCV's functions. They implement "saturating" math.
new = cv.add(grey, value)
Documentation for cv.add
When you only write new = grey + value, that isn't OpenCV doing the work, that is numpy doing the work. And numpy does nothing special. Wrap-around for integers is standard behavior.
An alternate approach that worked efficiently for me is to "blend in" a white image to the original image using the blend function in the PIL>Image library.
from PIL import Image
correctionVal = 0.05 # fraction of white to add to the main image
img_file = Image.open(location_filename)
img_file_white = Image.new("RGB", (width, height), "white")
img_blended = Image.blend(img_file, img_file_white, correctionVal)
img_blended = img_file * (1 - correctionVal) + img_file_white * correctionVal
Hence, if correctionVal = 0, we get the original image, and if correctionVal = 1, we get pure white.
This function self-corrects for RGB values exceeding 255.
Blending in black (RGB 0, 0, 0) reduces brightness.
I ran into a similar issue, but instead of addition, it was scaling image pixels in non-uniform manner.
The 1-D version of this:
a=np.array([100,200,250,252,255],dtype=np.uint8)
scaling=array([ 1.1, 1.2, 1.4, 1.2, 1.1])
result=np.uint8(a*scaling)
This gets you the overflow issue, of course; the result:
array([110, 240, 94, 46, 24], dtype=uint8)
The np.where works:
result_lim=np.where(a*scaling<=255,a*scaling,255)
yields result_lim as:
array([ 110., 240., 255., 255., 255.])
I was wondering about timing, I did this test on a 4000 x 6000 image (instead of 1D array), and found the np.where(), at least for my conditions, took about 2.5x times as long. Didn't know if there was a better/faster way of doing this. The option of converting to float, doing the operation, and then clipping as noted above was a bit slower than the np.where() method.
Don't know if there are better methods for this.