Skip to content

Commit bf5c52b

Browse files
authored
Merge pull request #21 from funkelab/add_area
Add area attribute to candidate graph nodes
2 parents 7ebd9c1 + 6ad4d55 commit bf5c52b

File tree

4 files changed

+34
-6
lines changed

4 files changed

+34
-6
lines changed

src/motile_toolbox/candidate_graph/graph_attributes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class NodeAttr(Enum):
1111
TIME = "time"
1212
SEG_ID = "seg_id"
1313
SEG_HYPO = "seg_hypo"
14+
AREA = "area"
1415

1516

1617
class EdgeAttr(Enum):

src/motile_toolbox/candidate_graph/utils.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,16 @@ def nodes_from_segmentation(
3838
segmentation: np.ndarray,
3939
scale: list[float] | None = None,
4040
) -> tuple[nx.DiGraph, dict[int, list[Any]]]:
41-
"""Extract candidate nodes from a segmentation. Also computes specified attributes.
42-
Returns a networkx graph with only nodes, and also a dictionary from frames to
43-
node_ids for efficient edge adding.
41+
"""Extract candidate nodes from a segmentation. Returns a networkx graph
42+
with only nodes, and also a dictionary from frames to node_ids for
43+
efficient edge adding.
44+
45+
Each node will have the following attributes (named as in NodeAttrs):
46+
- time
47+
- position
48+
- segmentation id
49+
- area
50+
- hypothesis id (optional)
4451
4552
Args:
4653
segmentation (np.ndarray): A numpy array with integer labels and dimensions
@@ -77,9 +84,7 @@ def nodes_from_segmentation(
7784
props = regionprops(hypo, spacing=tuple(scale[1:]))
7885
for regionprop in props:
7986
node_id = get_node_id(t, regionprop.label, hypothesis_id=hypo_id)
80-
attrs = {
81-
NodeAttr.TIME.value: t,
82-
}
87+
attrs = {NodeAttr.TIME.value: t, NodeAttr.AREA.value: regionprop.area}
8388
attrs[NodeAttr.SEG_ID.value] = regionprop.label
8489
if hypo_id is not None:
8590
attrs[NodeAttr.SEG_HYPO.value] = hypo_id

tests/conftest.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def graph_2d():
7171
NodeAttr.POS.value: (50, 50),
7272
NodeAttr.TIME.value: 0,
7373
NodeAttr.SEG_ID.value: 1,
74+
NodeAttr.AREA.value: 1245,
7475
},
7576
),
7677
(
@@ -79,6 +80,7 @@ def graph_2d():
7980
NodeAttr.POS.value: (20, 80),
8081
NodeAttr.TIME.value: 1,
8182
NodeAttr.SEG_ID.value: 1,
83+
NodeAttr.AREA.value: 305,
8284
},
8385
),
8486
(
@@ -87,6 +89,7 @@ def graph_2d():
8789
NodeAttr.POS.value: (60, 45),
8890
NodeAttr.TIME.value: 1,
8991
NodeAttr.SEG_ID.value: 2,
92+
NodeAttr.AREA.value: 697,
9093
},
9194
),
9295
]
@@ -110,6 +113,7 @@ def multi_hypothesis_graph_2d():
110113
NodeAttr.TIME.value: 0,
111114
NodeAttr.SEG_HYPO.value: 0,
112115
NodeAttr.SEG_ID.value: 1,
116+
NodeAttr.AREA.value: 1245,
113117
},
114118
),
115119
(
@@ -119,6 +123,7 @@ def multi_hypothesis_graph_2d():
119123
NodeAttr.TIME.value: 0,
120124
NodeAttr.SEG_HYPO.value: 1,
121125
NodeAttr.SEG_ID.value: 1,
126+
NodeAttr.AREA.value: 697,
122127
},
123128
),
124129
(
@@ -128,6 +133,7 @@ def multi_hypothesis_graph_2d():
128133
NodeAttr.TIME.value: 1,
129134
NodeAttr.SEG_HYPO.value: 0,
130135
NodeAttr.SEG_ID.value: 1,
136+
NodeAttr.AREA.value: 305,
131137
},
132138
),
133139
(
@@ -137,6 +143,7 @@ def multi_hypothesis_graph_2d():
137143
NodeAttr.TIME.value: 1,
138144
NodeAttr.SEG_HYPO.value: 1,
139145
NodeAttr.SEG_ID.value: 1,
146+
NodeAttr.AREA.value: 697,
140147
},
141148
),
142149
(
@@ -146,6 +153,7 @@ def multi_hypothesis_graph_2d():
146153
NodeAttr.TIME.value: 1,
147154
NodeAttr.SEG_HYPO.value: 0,
148155
NodeAttr.SEG_ID.value: 2,
156+
NodeAttr.AREA.value: 697,
149157
},
150158
),
151159
(
@@ -155,6 +163,7 @@ def multi_hypothesis_graph_2d():
155163
NodeAttr.TIME.value: 1,
156164
NodeAttr.SEG_HYPO.value: 1,
157165
NodeAttr.SEG_ID.value: 2,
166+
NodeAttr.AREA.value: 1245,
158167
},
159168
),
160169
]
@@ -256,6 +265,7 @@ def graph_3d():
256265
NodeAttr.POS.value: (50, 50, 50),
257266
NodeAttr.TIME.value: 0,
258267
NodeAttr.SEG_ID.value: 1,
268+
NodeAttr.AREA.value: 33401,
259269
},
260270
),
261271
(
@@ -264,6 +274,7 @@ def graph_3d():
264274
NodeAttr.POS.value: (20, 50, 80),
265275
NodeAttr.TIME.value: 1,
266276
NodeAttr.SEG_ID.value: 1,
277+
NodeAttr.AREA.value: 4169,
267278
},
268279
),
269280
(
@@ -272,6 +283,7 @@ def graph_3d():
272283
NodeAttr.POS.value: (60, 50, 45),
273284
NodeAttr.TIME.value: 1,
274285
NodeAttr.SEG_ID.value: 2,
286+
NodeAttr.AREA.value: 14147,
275287
},
276288
),
277289
]
@@ -297,6 +309,7 @@ def multi_hypothesis_graph_3d():
297309
NodeAttr.TIME.value: 0,
298310
NodeAttr.SEG_HYPO.value: 0,
299311
NodeAttr.SEG_ID.value: 1,
312+
NodeAttr.AREA.value: 305,
300313
},
301314
),
302315
(
@@ -306,6 +319,7 @@ def multi_hypothesis_graph_3d():
306319
NodeAttr.TIME.value: 1,
307320
NodeAttr.SEG_HYPO.value: 1,
308321
NodeAttr.SEG_ID.value: 1,
322+
NodeAttr.AREA.value: 305,
309323
},
310324
),
311325
(
@@ -315,6 +329,7 @@ def multi_hypothesis_graph_3d():
315329
NodeAttr.TIME.value: 1,
316330
NodeAttr.SEG_HYPO.value: 0,
317331
NodeAttr.SEG_ID.value: 1,
332+
NodeAttr.AREA.value: 305,
318333
},
319334
),
320335
(
@@ -324,6 +339,7 @@ def multi_hypothesis_graph_3d():
324339
NodeAttr.TIME.value: 1,
325340
NodeAttr.SEG_HYPO.value: 0,
326341
NodeAttr.SEG_ID.value: 2,
342+
NodeAttr.AREA.value: 305,
327343
},
328344
),
329345
(
@@ -333,6 +349,7 @@ def multi_hypothesis_graph_3d():
333349
NodeAttr.TIME.value: 1,
334350
NodeAttr.SEG_HYPO.value: 1,
335351
NodeAttr.SEG_ID.value: 1,
352+
NodeAttr.AREA.value: 305,
336353
},
337354
),
338355
]

tests/test_candidate_graph/test_utils.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def test_nodes_from_segmentation_2d(segmentation_2d):
3232
assert Counter(list(node_graph.nodes)) == Counter(["0_1", "1_1", "1_2"])
3333
assert node_graph.nodes["1_1"][NodeAttr.SEG_ID.value] == 1
3434
assert node_graph.nodes["1_1"][NodeAttr.TIME.value] == 1
35+
assert node_graph.nodes["1_1"][NodeAttr.AREA.value] == 305
3536
assert node_graph.nodes["1_1"][NodeAttr.POS.value] == (20, 80)
3637

3738
assert node_frame_dict[0] == ["0_1"]
@@ -44,6 +45,7 @@ def test_nodes_from_segmentation_2d(segmentation_2d):
4445
assert Counter(list(node_graph.nodes)) == Counter(["0_1", "1_1", "1_2"])
4546
assert node_graph.nodes["1_1"][NodeAttr.SEG_ID.value] == 1
4647
assert node_graph.nodes["1_1"][NodeAttr.TIME.value] == 1
48+
assert node_graph.nodes["1_1"][NodeAttr.AREA.value] == 610
4749
assert node_graph.nodes["1_1"][NodeAttr.POS.value] == (20, 160)
4850

4951
assert node_frame_dict[0] == ["0_1"]
@@ -63,6 +65,7 @@ def test_nodes_from_segmentation_2d_hypo(
6365
assert node_graph.nodes["1_0_1"][NodeAttr.SEG_ID.value] == 1
6466
assert node_graph.nodes["1_0_1"][NodeAttr.SEG_HYPO.value] == 0
6567
assert node_graph.nodes["1_0_1"][NodeAttr.TIME.value] == 1
68+
assert node_graph.nodes["1_0_1"][NodeAttr.AREA.value] == 305
6669
assert node_graph.nodes["1_0_1"][NodeAttr.POS.value] == (20, 80)
6770

6871
assert Counter(node_frame_dict[0]) == Counter(["0_0_1", "0_1_1"])
@@ -77,6 +80,7 @@ def test_nodes_from_segmentation_3d(segmentation_3d):
7780
assert Counter(list(node_graph.nodes)) == Counter(["0_1", "1_1", "1_2"])
7881
assert node_graph.nodes["1_1"][NodeAttr.SEG_ID.value] == 1
7982
assert node_graph.nodes["1_1"][NodeAttr.TIME.value] == 1
83+
assert node_graph.nodes["1_1"][NodeAttr.AREA.value] == 4169
8084
assert node_graph.nodes["1_1"][NodeAttr.POS.value] == (20, 50, 80)
8185

8286
assert node_frame_dict[0] == ["0_1"]
@@ -88,6 +92,7 @@ def test_nodes_from_segmentation_3d(segmentation_3d):
8892
)
8993
assert Counter(list(node_graph.nodes)) == Counter(["0_1", "1_1", "1_2"])
9094
assert node_graph.nodes["1_1"][NodeAttr.SEG_ID.value] == 1
95+
assert node_graph.nodes["1_1"][NodeAttr.AREA.value] == 4169 * 4.5
9196
assert node_graph.nodes["1_1"][NodeAttr.TIME.value] == 1
9297
assert node_graph.nodes["1_1"][NodeAttr.POS.value] == (20.0, 225.0, 80.0)
9398

0 commit comments

Comments
 (0)