Skip to content

Commit e399302

Browse files
Donglai Weiclaude
andcommitted
Fix region graph unpacking: use ScoredEdge dict format, not numpy tuples
The waterz agglomerate.pyx getRegionGraph() returns a list of ScoredEdge dicts {u, v, score} with float scores, not (rg_id, rg_sc) numpy arrays. Fix unpacking and invert OneMinus float scores (1.0 - score) to get raw affinities for merge_segments. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 9bb31b1 commit e399302

2 files changed

Lines changed: 21 additions & 11 deletions

File tree

connectomics/decoding/decoders/waterz.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -306,18 +306,30 @@ def decode_waterz(
306306
processed: List[np.ndarray] = []
307307
for waterz_result in seg_list:
308308
if do_dust_merge:
309-
seg, (rg_id, rg_sc) = waterz_result
309+
seg, region_graph = waterz_result
310310
else:
311311
seg = waterz_result
312312

313313
# Size+affinity dust merge reusing the agglomeration's region graph.
314-
# rg_sc is uint8 sorted ascending (low score = high affinity).
315-
# Invert OneMinus/One255Minus scores to raw affinities in [0, 1].
314+
# region_graph is a list of ScoredEdge dicts {u, v, score}.
315+
# Scores use OneMinus/One255Minus: low score = high affinity.
316+
# Invert to raw affinities and sort descending for merge_segments.
316317
if do_dust_merge:
317318
seg = seg.astype(np.uint64, copy=False)
318-
rg_affs = (255.0 - rg_sc.astype(np.float32)) / 255.0
319-
id1 = rg_id[:, 0].astype(np.uint64)
320-
id2 = rg_id[:, 1].astype(np.uint64)
319+
n_edges = len(region_graph)
320+
rg_affs = np.empty(n_edges, dtype=np.float32)
321+
id1 = np.empty(n_edges, dtype=np.uint64)
322+
id2 = np.empty(n_edges, dtype=np.uint64)
323+
for idx, edge in enumerate(region_graph):
324+
rg_affs[idx] = 1.0 - float(edge["score"])
325+
id1[idx] = int(edge["u"])
326+
id2[idx] = int(edge["v"])
327+
if n_edges:
328+
np.clip(rg_affs, 0.0, 1.0, out=rg_affs)
329+
order = np.argsort(rg_affs)[::-1]
330+
rg_affs = np.ascontiguousarray(rg_affs[order])
331+
id1 = np.ascontiguousarray(id1[order])
332+
id2 = np.ascontiguousarray(id2[order])
321333
ids, cnts = np.unique(seg, return_counts=True)
322334
max_id = int(ids.max()) if len(ids) else 0
323335
counts = np.zeros(max_id + 1, dtype=np.uint64)

tests/unit/test_decode_waterz.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,9 @@ def waterz(self, affs, thresholds, **kwargs):
2121
seg[:, :, :2] = 1
2222
seg[:, :, 2:] = 2
2323
if kwargs.get("return_region_graph", False):
24-
# rgToArr format: (rg_id (N,2) uint32, rg_sc (N,) uint8)
25-
# score=51 → affinity = (255-51)/255 ≈ 0.8
26-
rg_id = np.array([[1, 2]], dtype=np.uint32)
27-
rg_sc = np.array([51], dtype=np.uint8)
28-
return [(seg.copy(), (rg_id.copy(), rg_sc.copy())) for _ in thresholds]
24+
# ScoredEdge dicts: score=0.2 → affinity = 1.0 - 0.2 = 0.8
25+
rg = [{"u": 1, "v": 2, "score": 0.2}]
26+
return [(seg.copy(), list(rg)) for _ in thresholds]
2927
return [seg.copy() for _ in thresholds]
3028

3129
def merge_dust(self, seg, affs, size_th, weight_th, dust_th):

0 commit comments

Comments
 (0)