Related
I have a matrix which only contains 0 or 1.
I also have a list of colors e.g. the below
class_colors = [[0,0,0], [0,255,0]]
m = np.random.random_integers(0,1,(5,5))
e.g. the m looks like:
0,1,0,1
0,1,0,1
0,1,0,1
How can I replace the 1-values in m with the class_colors[1] and
0-values in m with class_colors[0] , so the m will look something like:
[0,0,0], [0,255,0],[0,0,0], [0,255,0]
[0,0,0], [0,255,0],[0,0,0], [0,255,0]
[0,0,0], [0,255,0],[0,0,0], [0,255,0]
I used to be able to do so with np.argmax() and np.take() but it requires the m looks like [class_num,w,h] and then I can do argmax with axis=0.
I know I could do it with for loop, but is there any better and faster approach to do this?
Is it what you expect:
>>> c[m]
array([[[ 0, 0, 0],
[ 0, 255, 0],
[ 0, 0, 0],
[ 0, 255, 0]],
[[ 0, 0, 0],
[ 0, 255, 0],
[ 0, 0, 0],
[ 0, 255, 0]]])
>>> c # c = np.array(class_colors)
array([[ 0, 0, 0],
[ 0, 255, 0]])
>>> m # m = np.random.randint(0, 2, (2, 4))
array([[0, 1, 0, 1],
[0, 1, 0, 1]])
By extension:
class_colors = [[0,0,0], [0,255,0], [255,0,0], [0,0,255]]
c = np.array(class_colors)
m = np.random.randint(0, 4, (2, 4))
# m <- array([[2, 1, 1, 3], [2, 3, 1, 0]])
>>> c[m]
array([[[255, 0, 0],
[ 0, 255, 0],
[ 0, 255, 0],
[ 0, 0, 255]],
[[255, 0, 0],
[ 0, 0, 255],
[ 0, 255, 0],
[ 0, 0, 0]]])
Not surprisingly the intuitive way works (using advanced indexing):
class_colors = np.array([[0,0,0], [0,255,0]])
m = np.array([0,1,0,1,0,1,0,1,0,1,0,1]).reshape((3,4))
class_colors[m]
array = np.array([\
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255],
[ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255],
[ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255],
[ 0, 0, 0, 255, 255, 255, 255, 255, 255, 255],
[ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255]])
The zeros define a shape:
My question is: How can I extract the indexes of the zeros which define the contour of the shape?
If you don't mind using scipy, you can use a 2D convolution to check if your zero values are surrounded by other zero values or not:
import numpy as np
import scipy.signal as signal
# Dummy input
A = np.array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 255],
[ 0, 0, 0, 0, 0, 0, 0, 255, 255, 255],
[ 0, 0, 0, 0, 0, 255, 255, 255, 255, 255],
[ 0, 0, 0, 255, 255, 255, 255, 255, 255, 255],
[ 0, 255, 255, 255, 255, 255, 255, 255, 255, 255]])
# We convolve the array with a 3x3 kernel filled with one,
# we use mode='same' in order to preserve the shape of the original array
# and we multiply the result by (A==0).
c2d = signal.convolve2d(A==0,np.ones((3,3)),mode='same')*(A==0)
# It is on the border if the values are > 0 and not equal to 9 so:
res = ((c2d>0) & (c2d<9)).astype(int)
# use np.where(res) if you need a linear index instead.
and we obtain the following boolean index:
array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[1, 0, 1, 1, 1, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
I would like to convert numpy array into numpy array of arrays.
I have an array: a = [[0,0,0],[0,255,0],[0,255,255],[255,255,255]]
and I would like to have: b = [[[0,0,0],[0,0,0],[0,0,0]],[[0,0,0],[255,255,255],[0,0,0]],[[0,0,0],[255,255,255],[255,255,255]],[[255,255,255],[255,255,255],[255,255,255]]]
Is there any easy way to do it?
I have tried with np.where(a == 0, [0,0,0],[255,255,255]) but I got the following error:
ValueError: operands could not be broadcast together with shapes
You can use broadcast_to as
b = np.broadcast_to(a, (3,4,3))
where a was shape (3,4). Then you need to swap the axes around
import numpy as np
a = np.array([[0,0,0],[0,255,0],[0,255,255],[255,255,255]])
b = np.broadcast_to(a, (3,4,3))
c = np.moveaxis(b, [0,1,2], [2,0,1])
c
giving
array([[[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]],
[[ 0, 0, 0],
[255, 255, 255],
[ 0, 0, 0]],
[[ 0, 0, 0],
[255, 255, 255],
[255, 255, 255]],
[[255, 255, 255],
[255, 255, 255],
[255, 255, 255]]])
A more direct method broadcasting method suggested by #Divakar is
b = np.broadcast(a[:,:,None], (4,3,3))
which produces the same output without axis swapping.
What you tried will work with the following small modification:
a = np.array(a)
np.where(a[...,None]==0,[0,0,0],[255,255,255])
To make multidimensional indexing available we have to cast a to array first. a[...,None] adds a new dimension at the end of a to accomodate the triplets 0,0,0 and 255,255,255.
In [204]: a = np.array([[0,0,0],[0,255,0],[0,255,255],[255,255,255]])
In [205]: a.shape
Out[205]: (4, 3)
Looks like you want to replicate each element 3 times, making a new trailing dimension. We can do that using repeat (after adding the new trailing dimension):
In [207]: a.reshape(4,3,1).repeat(3,2)
Out[207]:
array([[[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0]],
[[ 0, 0, 0],
[255, 255, 255],
[ 0, 0, 0]],
[[ 0, 0, 0],
[255, 255, 255],
[255, 255, 255]],
[[255, 255, 255],
[255, 255, 255],
[255, 255, 255]]])
In [208]: _.shape
Out[208]: (4, 3, 3)
Simply put, what I'm trying to do is similar to this question: Convert RGB image to index image, but instead of 1-channel index image, I want to get n-channel image where img[h, w] is a one-hot encoded vector. For example, if the input image is [[[0, 0, 0], [255, 255, 255]], and index 0 is assigned to black and 1 is assigned to white, then the desired output is [[[1, 0], [0, 1]]].
Like the previous person asked the question, I have implemented this naively, but the code runs quite slowly, and I believe a proper solution using numpy would be significantly faster.
Also, as suggested in the previous post, I can preprocess each image into grayscale and one-hot encode the image, but I want a more general solution.
Example
Say I want to assign white to 0, red to 1, blue to 2, and yellow to 3:
(255, 255, 255): 0
(255, 0, 0): 1
(0, 0, 255): 2
(255, 255, 0): 3
, and I have an image which consists of those four colors, where image is a 3D array containing R, G, B values for each pixel:
[
[[255, 255, 255], [255, 255, 255], [255, 0, 0], [255, 0, 0]],
[[ 0, 0, 255], [255, 255, 255], [255, 0, 0], [255, 0, 0]],
[[ 0, 0, 255], [ 0, 0, 255], [255, 255, 255], [255, 255, 255]],
[[255, 255, 255], [255, 255, 255], [255, 255, 0], [255, 255, 0]]
]
, and this is what I want to get where each pixel is changed to one-hot encoded values of index. (Since changing a 2d array of index values to 3d array of one-hot encoded values is easy, getting a 2d array of index values is fine too.)
[
[[1, 0, 0, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]],
[[0, 0, 1, 0], [1, 0, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]],
[[0, 0, 1, 0], [0, 0, 1, 0], [1, 0, 0, 0], [1, 0, 0, 0]],
[[1, 0, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 1]]
]
In this example I used colors where RGB components are either 255 or 0, but I don't want to solutions rely on that fact.
My solution looks like this and should work for arbitrary colors:
color_dict = {0: (0, 255, 255),
1: (255, 255, 0),
....}
def rgb_to_onehot(rgb_arr, color_dict):
num_classes = len(color_dict)
shape = rgb_arr.shape[:2]+(num_classes,)
arr = np.zeros( shape, dtype=np.int8 )
for i, cls in enumerate(color_dict):
arr[:,:,i] = np.all(rgb_arr.reshape( (-1,3) ) == color_dict[i], axis=1).reshape(shape[:2])
return arr
def onehot_to_rgb(onehot, color_dict):
single_layer = np.argmax(onehot, axis=-1)
output = np.zeros( onehot.shape[:2]+(3,) )
for k in color_dict.keys():
output[single_layer==k] = color_dict[k]
return np.uint8(output)
I haven't tested it for speed yet, but at least, it works :)
We could generate the decimal equivalents of each pixel color. With each channel having 0 or 255 as the value, there would be total 8 possibilities, but it seems we are only interested in four of those colors.
Then, we would have two ways to solve it :
One would involve making unique indices from those decimal equivalents starting from 0 till the final color, all in sequence and finally initializing an output array and assigning into it.
Other way would be to use broadcasted comparisons of those decimal equivalents against the colors.
These two methods are listed next -
def indexing_based(a):
b = (a == 255).dot([4,2,1]) # Decimal equivalents
colors = np.array([7,4,1,6]) # Define colors decimal equivalents here
idx = np.empty(colors.max()+1,dtype=int)
idx[colors] = np.arange(len(colors))
m,n,r = a.shape
out = np.zeros((m,n,len(colors)), dtype=int)
out[np.arange(m)[:,None], np.arange(n), idx[b]] = 1
return out
def broadcasting_based(a):
b = (a == 255).dot([4,2,1]) # Decimal equivalents
colors = np.array([7,4,1,6]) # Define colors decimal equivalents here
return (b[...,None] == colors).astype(int)
Sample run -
>>> a = np.array([
... [[255, 255, 255], [255, 255, 255], [255, 0, 0], [255, 0, 0]],
... [[ 0, 0, 255], [255, 255, 255], [255, 0, 0], [255, 0, 0]],
... [[ 0, 0, 255], [ 0, 0, 255], [255, 255, 255], [255, 255, 255]],
... [[255, 255, 255], [255, 255, 255], [255, 255, 0], [255, 255, 0]],
... [[255, 255, 255], [255, 0, 0], [255, 255, 0], [255, 0 , 0]]])
>>> indexing_based(a)
array([[[1, 0, 0, 0],
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0]],
[[0, 0, 1, 0],
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0]],
[[0, 0, 1, 0],
[0, 0, 1, 0],
[1, 0, 0, 0],
[1, 0, 0, 0]],
[[1, 0, 0, 0],
[1, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 0, 1]],
[[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 1, 0, 0]]])
>>> np.allclose(broadcasting_based(a), indexing_based(a))
True
A simple implementation involves masking the relevant pixel positions, whether it's for converting from label to color or vice-versa. I show here how to convert between dense (1-channel labels), OHE (one-hot-encoding sparse), and RGB formats. Essentially performing OHE<->RGB<->dense.
Having defined your RGB-encoded input as rgb.
First define the color label to color mapping (no need for a dict here):
>>> colors = np.array([[ 255, 255, 255],
[ 255, 0, 0],
[ 0, 0, 255],
[ 255, 255, 0]])
RGB (h, w, 3) to dense (h, w)
dense = np.zeros(seg.shape[:2])
for label, color in enumerate(colors):
dense[np.all(seg == color, axis=-1)] = label
RGB (h, w, 3) to OHE (h, w, #classes)
Similar to the previous conversion, RGB to one-hot-encoding requires two additional lines:
ohe = np.zeros((*seg.shape[:2], len(colors)))
for label, color in enumerate(colors):
v = np.zeros(len(colors))
v[label] = 1
ohe[np.all(seg == color, axis=-1)] = v
dense (h, w) to RGB (h, w, 3)
rgb = np.zeros((*labels.shape, 3))
for label, color in enumerate(colors):
rgb[labels == label] = color
OHE (h, w, #classes) to RGB (h, w, 3)
Converting from OHE to dense requires one line:
dense = ohe.argmax(-1)
Then you can simply follow dense->RGB.
I'm new to numpy and trying to understand the following example from here. I'm having trouble understanding the output of
>>> palette[image]
When the indexed array a is multidimensional, a single array of indices refers to the first dimension of a. The following example shows this behavior by converting an image of labels into a color image using a palette.
>>> palette = array( [ [0,0,0], # black
... [255,0,0], # red
... [0,255,0], # green
... [0,0,255], # blue
... [255,255,255] ] ) # white
>>> image = array( [ [ 0, 1, 2, 0 ], # each value corresponds to a color in the palette
... [ 0, 3, 4, 0 ] ] )
>>> palette[image] # the (2,4,3) color image
array([[[ 0, 0, 0],
[255, 0, 0],
[ 0, 255, 0],
[ 0, 0, 0]],
[[ 0, 0, 0],
[ 0, 0, 255],
[255, 255, 255],
[ 0, 0, 0]]])
You are creating a 3D array, where first 2D array (withing 3D array) is given by extracting rows from palette as given by indices of image[0] and the second array is given by extracting rows from palette as given by indices of image[1].
>>> palette = array( [ [0,0,0], # black
... [255,0,0], # red
... [0,255,0], # green
... [0,0,255], # blue
... [255,255,255] ] ) # white
>>> image = array( [ [ 0, 1, 2, 0 ], # each value corresponds to a color in the palette
... [ 0, 3, 4, 0 ] ] )
>>> palette[image] # the (2,4,3) color image
array([[[ 0, 0, 0], # row at index 0 of palete
[255, 0, 0], # index 1
[ 0, 255, 0], # index 2
[ 0, 0, 0]], # index 0
[[ 0, 0, 0], # index 0
[ 0, 0, 255], # index 3
[255, 255, 255], # index 4
[ 0, 0, 0]]]) # index 0
This might help you understand:
array([[[ 0, 0, 0], # palette[0]
[255, 0, 0], # palette[1]
[ 0, 255, 0], # palette[2]
[ 0, 0, 0]], # palette[0]
[[ 0, 0, 0], # palette[0]
[ 0, 0, 255], # palette[3]
[255, 255, 255], # palette[4]
[ 0, 0, 0]]]) # palette[0]