Array Indexing in multi dimensional numpy array - python

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]

Related

How to use Numpy to replace values in a matrix with a color e.g.[0,255,0]

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]

Is there a function which apply a displacement map / matrix on an image?

I generate a displacement map / matrix of size (H, W, 2), each channel corresponding to the displacements of values in x and in y. The goal is to "move" the pixels values of a given image of shape (H, W, 3), with the values of the displacement map.
A simple example with only one channel (x for example) would be :
disp_map = np.array([[ 0, 1, 1, 0 ],
[0, 0, 0, 0],
[1, 1, 0, 0]])
im = np.array([[ 0, 1, 1, 0 ],
[0, 1, 1, 0],
[0, 0, 0, 0]])
output = apply_disp_function(im, disp_map)
output
np.array([[ 1, 1, 0, 0 ],
[0, 1, 1, 0],
[0, 0, 0, 0]])
I don't know if there are already a function which does it or I have to write a function in numpy with slicing.
Here is one possible solution. Here the disp_map array is supposed to have one map for each axis in its last dimension. Displacements beyond the boundaries of the image would wrap around.
import numpy as np
def displace(im, disp_map):
im = np.asarray(im)
disp_map = np.asarray(disp_map)
grid = np.ogrid[list(map(slice, disp_map.shape[:-1]))]
result = np.zeros_like(im)
np.add.at(result, tuple((g + disp_map[..., i]) % im.shape[i]
for i, g in enumerate(grid)), im)
return result
# Each pair of values represents the number of rows and columns
# that each element will be displaced
disp_map = np.array([[[ 0, 0], [ 0, -1], [ 0, 1], [ 0, 0]],
[[ 0, 0], [ 1, 0], [ 1, 1], [ 0, 0]],
[[ 0, 1], [ 0, 1], [ 0, 0], [ 0, 0]]])
im = np.array([[ 0, 1, 1, 0],
[ 0, 1, 1, 0],
[ 0, 0, 0, 0]])
output = displace(im, disp_map)
print(output)
# [[1 0 0 1]
# [0 0 0 0]
# [0 1 0 1]]

Calculate mask from comparing values of two Numpy arrays that are different shapes

Given these two arrays:
a = np.array(
[
[
[1, 102, 103, 255],
[201, 2, 202, 255],
[201, 202, 202, 255]
],
[
[11, 120, 0, 255],
[0, 0, 0, 255],
[1, 22, 142, 255]
],
])
b = np.array(
[
[
[1, 102, 103, 255],
[201, 2, 202, 255]
],
[
[11, 120, 0, 255],
[221, 222, 13, 255]
],
[
[91, 52, 53, 255],
[0, 0, 0, 255]
],
])
a.shape # => (2, 3, 4)
b.shape # => (3, 3, 4)
I want to overlay a and b at 0, 0 and output a mask that represents when a values equal b values. The values compared are the full pixel values, so in this case [1, 102, 103, 255] is a value.
An output mask like this would be great:
result = np.array([
[
true,
true,
false
],
[
true,
false,
false
],
[
false,
false,
false
],
])
A perfect answer in my case would be where matching values become [255, 0, 0, 255] and mismatching values become [0, 0, 0, 0]:
result = np.array([
[
[255, 0, 0, 255],
[255, 0, 0, 255],
[0, 0, 0, 0]
],
[
[255, 0, 0, 255],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
])
result.shape # => (3, 3, 4)
It looks like this:
[![diff between a and b][1]][1]
Here is one possibility using slicing.
outer = np.maximum(a.shape, b.shape)
inner = *map(slice, np.minimum(a.shape, b.shape)),
out = np.zeros(outer, np.result_type(a, b))
out[inner][(a[inner]==b[inner]).all(2)] = 255,0,0,255
out
# array([[[255, 0, 0, 255],
# [255, 0, 0, 255],
# [ 0, 0, 0, 0]],
#
# [[255, 0, 0, 255],
# [ 0, 0, 0, 0],
# [ 0, 0, 0, 0]],
#
# [[ 0, 0, 0, 0],
# [ 0, 0, 0, 0],
# [ 0, 0, 0, 0]]])

Replace multiple values in Numpy Array

Given the following example array:
import numpy as np
example = np.array(
[[[ 0, 0, 0, 255],
[ 0, 0, 0, 255]],
[[ 0, 0, 0, 255],
[ 221, 222, 13, 255]],
[[-166, -205, -204, 255],
[-257, -257, -257, 255]]]
)
I want to replace values [0, 0, 0, 255] values with [255, 0, 0, 255] and everything else becomes [0, 0, 0, 0].
So the desired output is:
[[[ 255, 0, 0, 255],
[ 255, 0, 0, 255]],
[[ 255, 0, 0, 255],
[ 0, 0, 0, 0]],
[[ 0, 0, 0, 0],
[ 0, 0, 0, 0]]
This solution got close:
np.place(example, example==[0, 0, 0, 255], [255, 0, 0, 255])
np.place(example, example!=[255, 0, 0, 255], [0, 0, 0, 0])
But it outputs this instead:
[[[255 0 0 255],
[255 0 0 255]],
[[255 0 0 255],
[ 0 0 0 255]], # <- extra 255 here
[[ 0 0 0 0],
[ 0 0 0 0]]]
What's a good way to do this?
You can find all occurrences of [0, 0, 0, 255] using
np.all(example == [0, 0, 0, 255], axis=-1)
# array([[ True, True],
# [ True, False],
# [False, False]])
Save these positions to a mask, set everything to zero, then place the desired output into the mask positions:
mask = np.all(example == [0, 0, 0, 255], axis=-1)
example[:] = 0
example[mask, :] = [255, 0, 0, 255]
# array([[[255, 0, 0, 255],
# [255, 0, 0, 255]],
#
# [[255, 0, 0, 255],
# [ 0, 0, 0, 0]],
#
# [[ 0, 0, 0, 0],
# [ 0, 0, 0, 0]]])
Here's one way:
a = np.array([0, 0, 0, 255])
replace = np.array([255, 0, 0, 255])
m = (example - a).any(-1)
np.logical_not(m)[...,None] * replace
array([[[255, 0, 0, 255],
[255, 0, 0, 255]],
[[255, 0, 0, 255],
[ 0, 0, 0, 0]],
[[ 0, 0, 0, 0],
[ 0, 0, 0, 0]]])

How to convert RGB image to one-hot encoded 3d array based on color using numpy?

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.

Categories