BBQuercus
BBQuercus

Reputation: 879

Relabel multiple values in 2D numpy arrays

I have an image (segmentation map) which can be simplified to the following:

import numpy as np

x, y = np.indices((80, 80))
x1, y1, x2, y2 = 28, 28, 44, 52
r1, r2 = 16, 20
mask_circle1 = (x - x1) ** 2 + (y - y1) ** 2 < r1 ** 2
mask_circle2 = (x - x2) ** 2 + (y - y2) ** 2 < r2 ** 2
image = np.max([mask_circle1, mask_circle2*2], axis=0)

This image contains two circles labeled 1 and 2 respectively. I now want to relabel all values according to a old_value : new_value map / dictionary I have.

relabels = {1: 7, 2: 3}

The naive solution to this problem would be the following:

labels = []
for old_val, new_val in relabels.items():
    labels.append(np.where(image==old_val, new_val, 0))
new_image = np.max(labels, axis=0)

This works flawlessly but is rather slow when scaled to larger arrays with more labels. Is there any way to vectorise this operation instead of processing all relabelling steps one by one?

Thanks in advance.

Upvotes: 0

Views: 82

Answers (2)

BBQuercus
BBQuercus

Reputation: 879

I found an even faster option that is slightly modified from here:

import numpy as np

# Replace values
new_image = np.vectorize(relabels.get)(image)

# Replace None / undefined in relabel
new_image = np.where(new_image == None, 0, new_image).astype(int)

Upvotes: 0

swag2198
swag2198

Reputation: 2696

You can use broadcasting for elementwise comparison followed by multiplying with new labels and then reducing by taking max() along axis 0:

import numpy as np
old_labels = np.array([1, 2])
new_labels = np.array([3, 7])

# generating some random image with entries 0, 1 and 2
image = np.random.randint(0, 3, (10, 10))
# transform labels 
new_image = ((image == old_labels[:, None, None]).astype(int) * new_labels[:, None, None]).max(axis = 0)

Sample I/O:

>>> image = np.random.randint(0, 3, (10, 10))
>>> image
 array([[2, 2, 2, 1, 2, 1, 0, 0, 0, 1],
        [1, 0, 0, 1, 2, 0, 2, 2, 1, 2],
        [1, 2, 2, 1, 2, 1, 2, 1, 1, 2],
        [1, 2, 0, 0, 2, 2, 1, 1, 0, 2],
        [0, 1, 2, 1, 2, 0, 2, 0, 0, 2],
        [0, 0, 0, 2, 0, 0, 0, 2, 0, 2],
        [0, 2, 1, 2, 2, 1, 0, 2, 0, 1],
        [1, 0, 2, 1, 1, 2, 1, 1, 2, 1],
        [2, 0, 2, 2, 1, 2, 0, 2, 0, 2],
        [1, 0, 0, 2, 1, 1, 2, 0, 1, 2]])

>>> new_image = ((image == old_labels[:, None, None]).astype(int) * new_labels[:, None, None]).max(axis = 0)
>>> new_image
 array([[7, 7, 7, 3, 7, 3, 0, 0, 0, 3],
        [3, 0, 0, 3, 7, 0, 7, 7, 3, 7],
        [3, 7, 7, 3, 7, 3, 7, 3, 3, 7],
        [3, 7, 0, 0, 7, 7, 3, 3, 0, 7],
        [0, 3, 7, 3, 7, 0, 7, 0, 0, 7],
        [0, 0, 0, 7, 0, 0, 0, 7, 0, 7],
        [0, 7, 3, 7, 7, 3, 0, 7, 0, 3],
        [3, 0, 7, 3, 3, 7, 3, 3, 7, 3],
        [7, 0, 7, 7, 3, 7, 0, 7, 0, 7],
        [3, 0, 0, 7, 3, 3, 7, 0, 3, 7]])

Note that here the label 0 stays the same before and after transformation. Since you did not have 0 in the dictionary relabels I assumed it stays the same. Otherwise just put 0 in the array old_labels and its corresponding new label in new_labels.

Upvotes: 1

Related Questions