Converting a NumPy array to a PIL image - python

I want to create a PIL image from a NumPy array. Here is my attempt:
# Create a NumPy array, which has four elements. The top-left should be pure
# red, the top-right should be pure blue, the bottom-left should be pure green,
# and the bottom-right should be yellow.
pixels = np.array([[[255, 0, 0], [0, 255, 0]], [[0, 0, 255], [255, 255, 0]]])
# Create a PIL image from the NumPy array
image = Image.fromarray(pixels, 'RGB')
# Print out the pixel values
print image.getpixel((0, 0))
print image.getpixel((0, 1))
print image.getpixel((1, 0))
print image.getpixel((1, 1))
# Save the image
image.save('image.png')
However, the print out gives the following:
(255, 0, 0)
(0, 0, 0)
(0, 0, 0)
(0, 0, 0)
And the saved image has pure red in the top-left, but all the other pixels are black. Why are these other pixels not retaining the colour I have assigned to them in the NumPy array?

The RGB mode is expecting 8-bit values, so just casting your array should fix the problem:
In [25]: image = Image.fromarray(pixels.astype('uint8'), 'RGB')
...:
...: # Print out the pixel values
...: print image.getpixel((0, 0))
...: print image.getpixel((0, 1))
...: print image.getpixel((1, 0))
...: print image.getpixel((1, 1))
...:
(255, 0, 0)
(0, 0, 255)
(0, 255, 0)
(255, 255, 0)

Your numpy array should be of the form:
[[[248 248 248] # R G B
[248 248 248]
[249 249 249]
...
[ 79 76 45]
[ 79 76 45]
[ 78 75 44]]
[[247 247 247]
[247 247 247]
[248 248 248]
...
[ 80 77 46]
[ 79 76 45]
[ 79 76 45]]
...
[[148 121 92]
[149 122 93]
[153 126 97]
...
[126 117 100]
[126 117 100]
[125 116 99]]]
Assuming you have your numpy array stored in np_arr, here is how to convert it to a pillow Image:
from PIL import Image
import numpy as np
new_im = Image.fromarray(np_arr)
To show the new image, use:
new_im.show()

Related

How get unique pixels from 2d numpy array?

I have 2d array with rgb pixel data (2 row with 3 pixel in a row).
[[[255, 255, 255],[3, 0, 2],[255, 255, 255]],[[255, 255, 255],[3, 0, 2],[255, 255, 255]]]
How can I get unique pixel? I want to get
[[255, 255, 255], [3, 0, 2]]
I am trying to use np.unique and np.transpose with np.reshape but I wasn't able to get the desired result.
Reshape the array to 2D and then use np.unique with axis=0
arr = np.array([[[255, 255, 255],[3, 0, 2],[255, 255, 255]],[[255, 255, 255],[3, 0, 2],[255, 255, 255]]])
shape = arr.shape
arr = arr.reshape((shape[0] * shape[1], shape[2]))
print(np.unique(arr, axis=0))
Output
[[ 3 0 2]
[255 255 255]]
How about this?
import itertools
np.unique(np.array(list(itertools.chain(*arr))), axis=0)
array([[ 3, 0, 2],
[255, 255, 255]])

Numpy array not copy

I am working with numpy and images. I have a big image which i want to process bit by bit.
So I want to create a reference to the original image, do something with it and move on. But when I change something in the frame the change does not transfer to the original, which is the opposite of everything I read online
for example here : https://stackoverflow.com/a/53939444/11333604 if you don't use copy if you change one array the other changes. That does NOT HAPPEN to me
here is some pseudo code that demonstrates my problem
import numpy as np
#create a "big" imgae total black
matrix = np.full((5,5),255,np.uint8)
# the frame that i want to process
h = 3
w = 3
sub = matrix[:h, :w]
#mask to make everything 0
mask = np.zeros((h,w),np.uint8)
sub = sub & mask
#also change something radom so i know the problem is not the & or the mask
sub[0][0] = 12
#sub is changes
print(sub)
#matrix is not
print(matrix)
output
[[12 0 0]
[ 0 0 0]
[ 0 0 0]]
[[255 255 255 255 255]
[255 255 255 255 255]
[255 255 255 255 255]
[255 255 255 255 255]
[255 255 255 255 255]]
I suspect it has something to do with my arrays being 2d but i cant think of how
That's because in this line
sub = sub & mask
you're making sub to "look" at some new array formed with sub & mask and it loses its connection to matrix. If you do this in-place instead
sub &= mask
then matrix will be affected too:
>>> matrix
array([[ 12, 0, 0, 255, 255],
[ 0, 0, 0, 255, 255],
[ 0, 0, 0, 255, 255],
[255, 255, 255, 255, 255],
[255, 255, 255, 255, 255]], dtype=uint8)

How to pre-process RGB segmentation mask for multi-class semantic segmentation?

I am working on a multiclass semantic segmentation dataset, the dataset has RGB ground truth segmentation masks for the original images. The dataset has 24 classes. The following table displays the classes and their respective RGB values:
name
r
g
b
unlabeled
0
0
0
paved-area
128
64
128
dirt
130
76
0
grass
0
102
0
gravel
112
103
87
water
28
42
168
rocks
48
41
30
pool
0
50
89
vegetation
107
142
35
roof
70
70
70
wall
102
102
156
window
254
228
12
door
254
148
12
fence
190
153
153
fence-pole
153
153
153
person
255
22
96
dog
102
51
0
car
9
143
150
bicycle
119
11
32
tree
51
51
0
bald-tree
190
250
190
ar-marker
112
150
146
obstacle
2
135
115
conflicting
255
0
0
Sample RGB Ground Truth Segmentation Mask Image:
There are 400 images in the dataset, each having a shape of (4000 px X 6000 px). The directory structure of the dataset is shown below:
dataset_folder
├── original_images
│ ├── 000.png
│ ├── 001.png
│ ├── ...
| ├── 399.png
| └── 400.png
└── masks
├── 000.png
├── 001.png
├── ...
├── 399.png
└── 400.png
I want to create semantic segmentation masks from the RGB masks, by assigning integer values to the pixels in the range 0-23 (where each integer represents a class) and save them to the working directory. Can someone please suggest an efficient code for this task?
I had a similar problem.
My solution is probably not the most efficient, but as there is no other answer, i share it anyway :
First get an array from the image, opening it with openCV for example..
For the example, let's make an "image" of 4*3 px with three channels:
img=np.array([[
[128, 64,128],
[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]],
[[128, 64,128],
[ 0,102, 0],
[ 0, 0, 0],
[ 0, 0, 0]],
[[130, 76, 0],
[130, 76, 0],
[130, 76, 0],
[130, 76, 0]]])
Make a dictionary of the RGB values associated with the mask's wanted value (i wrote it down by hand for the example, but you can do it using pandas if you have a table as shown above), then make a list of the values encountered in the image, and finally create the mask with the corresponding categorical value.
unlabeled = str([0, 0, 0])
paved_area = str([128, 64, 128])
dirt = str([130, 76, 0])
grass = str([ 0, 102, 0])
labels = {unlabeled:0, paved_area:1, dirt:2, grass:3}
print(labels)
>>> {'[0, 0, 0]': 0, '[128, 64, 128]': 1, '[130, 76, 0]': 2, '[0, 102, 0]': 3}
width = img.shape[1]
height = img.shape[0]
values = [str(list(img[i,j])) for i in range(height) for j in range(width)]
print(values)
>>> ['[128, 64, 128]', '[0, 0, 0]', ..., '[130, 76, 0]']
print(len(values))
>>> 12 # width*height
mask=list([0]*width*height)
for i, value in enumerate(values):
mask[i]=labels[value]
mask = np.asarray(mask).reshape(height,width)
print(mask)
>>> array([[1, 0, 0, 0],
[1, 3, 0, 0],
[2, 2, 2, 2]])

How to use numpy.where to change all pixels of an image?

I have an image of shape (300,300,3) consisting of these pixels [255, 194, 7],[224, 255, 8],[230, 230, 230],[11, 102, 255]. I want to change this pixel [230, 230, 230] to [255,255,255]. And rest other pixels to [0,0,0]. So I'm applying numpy where function to switch the pixels. Below is the code:
import numpy
im = numpy.array([[[255, 194, 7],[224, 255, 8],[230, 230, 230],[11, 102, 255]]])
im[np.where((im == [230, 230, 230]).all(axis = 2))] = [255,255,255]
im[np.where((im != [255,255,255]).all(axis = 2))] = [0,0,0]
The first code is working fine, but all the pixels that have 255 in it like [11, 102, 255] doesnot get flipped at all in the second line. and the image remains same. Can anyone tell me what I'm doing wrong ?
import numpy as np
im = np.array([[[255, 194, 7],[224, 255, 8],[230, 230, 230],[11, 102, 255]]])
Like this?
Make a mask and use it to change the values.
>>> mask = im == 230
>>> im[mask] = 255
>>> im[np.logical_not(mask)] = 0
>>> im
=> array([[[ 0, 0, 0],
[ 0, 0, 0],
[255, 255, 255],
[ 0, 0, 0]]])
Or using numpy.where
>>> np.where(im==230, 255, 0)
=> array([[[ 0, 0, 0],
[ 0, 0, 0],
[255, 255, 255],
[ 0, 0, 0]]])
try
np.array_equal(arr1, arr2)

How to create a discrete RGB colourmap with N colours using numpy

I am trying to create a really simple colourmap using RGB triples. That has a discrete number of colours.
Here is the shape that I am trying to make it follow:
I know it is probably not a true RGB spectrum, but i wanted it to be as easy to compute as possible.
I want to be able to generate an array of N 8-bit RGB tuples that are evenly spaced along the line.
For easy instances where all of the points in the image above fall on colours, it is easy to generate using numpy.linspace() using the following code:
import numpy as np
# number of discrete colours
N = 9
# generate R array
R = np.zeros(N).astype(np.uint8)
R[:int(N/4)] = 255
R[int(N/4):int(2*N/4)+1] = np.linspace(255,0,num=(N/4)+1,endpoint=True)
# generate G array
G = 255*np.ones(N).astype(np.uint8)
G[0:int(N/4)+1] = np.linspace(0,255,num=(N/4)+1,endpoint=True)
G[int(3*N/4):] = np.linspace(255,0,num=(N/4)+1,endpoint=True)
# generate B array
B = np.zeros(N).astype(np.uint8)
B[int(2*N/4):int(3*N/4)+1] = np.linspace(0,255,num=(N/4)+1,endpoint=True)
B[int(3*N/4)+1:] = 255
# stack arrays
RGB = np.dstack((R,G,B))[0]
This code works fine for 5 colours:
r 255 255 0 0 0
g 0 255 255 255 0
b 0 0 0 255 255
9 colours:
r 255 255 255 127 0 0 0 0 0
g 0 127 255 255 255 255 255 127 0
b 0 0 0 0 0 127 255 255 255
13 colours:
r 255 255 255 255 170 85 0 0 0 0 0 0 0
g 0 85 170 255 255 255 255 255 255 255 170 85 0
b 0 0 0 0 0 0 0 85 170 255 255 255 255
etc.. but I'm having trouble working out how to make it work for an arbitrary N number of colours because the linspace trick only works if it is going from one endpoint to another.
Can someone please help me with working out how to do it? Any ideas for how to make my code above more efficient would be great as well, I am just learning how to use numpy after putting it off for ages..
Here is one reasonably convenient method using np.clip:
def spec(N):
t = np.linspace(-510, 510, N)
return np.round(np.clip(np.stack([-t, 510-np.abs(t), t], axis=1), 0, 255)).astype(np.uint8)
It avoids the problem you describe by only relying on the only two points that are guaranteed to be on the grid for any N which are the first and last points.
Examples:
>>> spec(5)
array([[255, 0, 0],
[255, 255, 0],
[ 0, 255, 0],
[ 0, 255, 255],
[ 0, 0, 255]], dtype=uint8)
>>> spec(10)
array([[255, 0, 0],
[255, 113, 0],
[255, 227, 0],
[170, 255, 0],
[ 57, 255, 0],
[ 0, 255, 57],
[ 0, 255, 170],
[ 0, 227, 255],
[ 0, 113, 255],
[ 0, 0, 255]], dtype=uint8)
If you just want RGB at given levels, the diagram that you have posted itself serves as the answer -
R = [255] * 256
R.extend(list(reversed(range(256))))
R.extend([0] * 256)
R.extend([0] * 256)
G = list(range(256))
G.extend([255] * 256)
G.extend([255] * 256)
G.extend(list(reversed(range(256))))
B = [0] * 256
B.extend([0] * 256)
B.extend(list(range(256)))
B.extend([255] * 256)
level = 5
step = 1024 // (level -1)
print(R[::step])
print(G[::step])
print(B[::step])

Categories

Resources