Python: Get the bounding box coordinates of each cluster in the split graph (2D numpy array)

Python: Get the bounding box coordinates of each cluster in the split graph (2D numpy array) … here is a solution to the problem.

Python: Get the bounding box coordinates of each cluster in the split graph (2D numpy array)

python :

I get a segmentation graph (

2D numpy array) with the class value (integers 0 to N) for each pixel of the original img, and I want to find the bounding box coordinates of each connected cluster in the segmentation graph.

EDIT: There may be multiple clusters per class in map!

I

think I could use something like `skimage.measure.label(seg_map, connectivity=1).`

Solution

There is an existing function `scipy.ndimage.measurements.find_objects` Allllmost is exactly what you need. Numpy and are required ` A little help from scipy.ndimage.measurements.label`, though:

``````import numpy as np
import scipy.ndimage.measurements as mnts

A = np.array([
[0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 2, 2, 0],
[0, 1, 1, 0, 2, 2, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 4, 4, 0, 1, 1, 0],
[0, 4, 4, 0, 1, 1, 0],
[4, 0, 0, 0, 0, 0, 1]
])

structure = np.array([
[1,1,1],
[1,1,1],
[1,1,1]
])

bboxSlices = {}
for i in range(1, A.max() + 1):
B = A.copy()
B[B != i] = 0

bboxSlices[i] = mnts.find_objects(mnts.label(B, structure=structure)[0])

print(bboxSlices)
``````

Output:

``````{1: [(slice(1, 3, None), slice(1, 3, None)),
(slice(4, 7, None), slice(4, 7, None))],
2: [(slice(1, 3, None), slice(4, 6, None))],
3: [],
4: [(slice(4, 7, None), slice(0, 3, None))]}
``````

`Each entry in the bboxSlices` dictionary is a tuple list. Each tuple contains two slices, a row slice and a column slice, each of which defines a bounding box around the cluster of corresponding classes.

Details

`label(...)` finds feature clusters and replaces their values with labels (for example, 1 for the first cluster, 2 for the second, and so on). `find_objects(...)` Then find the bounding box around each label. The problem is that `the label` treats all non-zero values as “features”. Therefore, for each class value i, we need a copy `of A` with all `non-i` values cleared.

`structure` defines the connectivity of the cluster. If you want a cluster that is not connected diagonally, you can use a different `structure`:

``````structure = np.array([
[0,1,0],
[1,1,1],
[0,1,0]
])
``````

This is easy if there is only 1 cluster per class :

``````import numpy as np

A = np.array([
[0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 2, 2, 0],
[0, 1, 1, 0, 2, 2, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 4, 4, 0, 3, 3, 0],
[0, 4, 4, 0, 3, 3, 0],
[0, 0, 0, 0, 0, 0, 0]
])

bboxCorners = {}
for i in range(1, A.max()+1):
B = np.argwhere(A==i)
bboxCorners[i] = B.min(0), B.max(0)

print(bboxCorners)
``````

Output:

``````{1: (array([1, 1]), array([2, 2])),
2: (array([1, 4]), array([2, 5])),
3: (array([4, 4]), array([5, 5])),
4: (array([4, 1]), array([5, 2]))}
``````