Replace multiple values in Numpy Array - python

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]]])

Related

Get contour indexes of subarray in array

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]])

Numpy array of integers into numpy array of arrays

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)

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]]])

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.

Array Indexing in multi dimensional numpy array

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]

Categories