1

I need to label an already classified img. The problem being, the image is non binary and I need to count separately neighbouring patch of different value.

Considering the following dataset :

import numpy as np 

data = np.zeros((6,6), dtype=np.uint16)
data[2:4, 2:4] = 10
data[4, 4] = 10
data[:2, :3] = 22
data[0, 5] = 22
data

>>>
array([[22, 22, 22,  0,  0, 22],
       [22, 22, 22,  0,  0,  0],
       [0,  0,  10, 10,  0,  0],
       [0,  0,  10, 10,  0,  0],
       [0,  0,   0,  0, 10,  0],
       [0,  0,   0,  0,  0,  0]], dtype=uint16)

I would like to obtain (with an 8 neigbours structuring element) the following :

array([[1, 1, 1, 0, 0, 3],
       [1, 1, 1, 0, 0, 0],
       [0, 0, 2, 2, 0, 0],
       [0, 0, 2, 2, 0, 0],
       [0, 0, 0, 0, 2, 0],
       [0, 0, 0, 0, 0, 0]], dtype=uint16)

but using the scipy.label function I obtain a complete different result :

from scipy import ndimage as ndi

s = ndi.generate_binary_structure(2,2)

labeled_array, num_features = ndi.label(data, structure=s)
labeled_array

>>>
array([[1, 1, 1, 0, 0, 2],
       [1, 1, 1, 0, 0, 0],
       [0, 0, 1, 1, 0, 0],
       [0, 0, 1, 1, 0, 0],
       [0, 0, 0, 0, 1, 0],
       [0, 0, 0, 0, 0, 0]], dtype=int16)

So is there a trick to separate patch of different value ?

Pierrick Rambaud
  • 1,726
  • 1
  • 20
  • 47
  • I don't understand what's your final goal: the desired result is identical to the given array with just label numbers `1` and `2` swapped. – Stef Jan 08 '21 at 15:54
  • that's unfortunate, I'll change the numbers to make myself clearer. I don't want the top left patch to be labeled with the middle one even if they touch. – Pierrick Rambaud Jan 08 '21 at 15:54

1 Answers1

2

Get a list of unique values uv and then replace each unique value with its order number (first value with 0, second with 1 etc.)

uv = np.unique(data)
res = np.select([data==i for i in uv], range(len(uv)))

Example:

import numpy as np 

data = np.zeros((6,6), dtype=np.uint16)
data[2:4, 2:4] = 10
data[4, 4] = 10
data[:2, :3] = 22
data[0, 5] = 32

Result:

array([[2, 2, 2, 0, 0, 3],
       [2, 2, 2, 0, 0, 0],
       [0, 0, 1, 1, 0, 0],
       [0, 0, 1, 1, 0, 0],
       [0, 0, 0, 0, 1, 0],
       [0, 0, 0, 0, 0, 0]])

UPDATE I've seen you changed your data in the question. In this case it's no longer an already classified image because data[0,5] can't be 22 if it's not linked with all the other 22s.
So in this case I guess you need to do the labelling for each unique entrie in data separately like so:

import numpy as np 
from scipy import ndimage as ndi

data = np.zeros((6,6), dtype=np.uint16)
data[2:4, 2:4] = 10
data[4, 4] = 10
data[:2, :3] = 22
data[0, 5] = 22

uv = np.unique(data)
s = ndi.generate_binary_structure(2,2)
cum_num = 0
result = np.zeros_like(data)
for v in uv[1:]:
    labeled_array, num_features = ndi.label((data==v).astype(int), structure=s)
    result += np.where(labeled_array > 0, labeled_array + cum_num, 0).astype(result.dtype)
    cum_num += num_features

Data:

[[22 22 22  0  0 22]
 [22 22 22  0  0  0]
 [ 0  0 10 10  0  0]
 [ 0  0 10 10  0  0]
 [ 0  0  0  0 10  0]
 [ 0  0  0  0  0  0]]

Result:

[[2 2 2 0 0 3]
 [2 2 2 0 0 0]
 [0 0 1 1 0 0]
 [0 0 1 1 0 0]
 [0 0 0 0 1 0]
 [0 0 0 0 0 0]]
Stef
  • 28,728
  • 2
  • 24
  • 52
  • I truggled a bit to find a simple use case... sorry. The image I have is classified in a GIS sense i.e. the RGB initial image is now classified into "areas" associated with a single value in one band. The original image is country sized with a resolution of 30m... not something I can easily share on github. In the end you nailed it – Pierrick Rambaud Jan 08 '21 at 18:23