Skip to content

Commit 6249766

Browse files
Update migration guide
1 parent b4055f2 commit 6249766

1 file changed

Lines changed: 74 additions & 0 deletions

File tree

MIGRATION_GUIDE.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,13 @@ Notes:
811811

812812
## Mutex Watershed
813813

814+
`bioimage-cpp` ships two mutex-watershed entry points, mirroring the two
815+
affogato APIs: one that consumes a dense affinity grid, and one that
816+
consumes an arbitrary graph with a separate list of mutex (long-range
817+
repulsive) edges.
818+
819+
### Grid-based mutex watershed (affinity volumes)
820+
814821
Affogato:
815822

816823
```python
@@ -853,6 +860,73 @@ Important migration notes:
853860
masked pixels are set to label `0`.
854861
- Output labels are `uint64`, consecutive, and 1-based for foreground pixels.
855862

863+
### Mutex watershed on a generic graph
864+
865+
For mutex watershed on an arbitrary undirected graph (region adjacency graph
866+
or otherwise) with a separate list of long-range repulsive edges,
867+
`bioimage-cpp` provides `bic.graph.mutex_watershed_clustering`. This is a
868+
port of affogato's `compute_mws_clustering` using the same input format as
869+
`LiftedMulticutObjective`: a base graph carries the attractive edges, and
870+
long-range (called *mutex* here) edges are supplied alongside as a `(M, 2)`
871+
node-pair array. The same `(graph, edge_costs, lifted_uvs, lifted_costs)`
872+
tuple used to build a lifted multicut problem can be passed to the mutex
873+
watershed clustering without any reshaping.
874+
875+
Affogato:
876+
877+
```python
878+
from affogato.segmentation import compute_mws_clustering
879+
880+
labels = compute_mws_clustering(
881+
number_of_nodes,
882+
uvs.astype(np.uint64),
883+
mutex_uvs.astype(np.uint64),
884+
weights.astype(np.float32),
885+
mutex_weights.astype(np.float32),
886+
)
887+
```
888+
889+
bioimage-cpp:
890+
891+
```python
892+
import bioimage_cpp as bic
893+
894+
graph = bic.graph.UndirectedGraph.from_edges(number_of_nodes, uvs)
895+
labels = bic.graph.mutex_watershed_clustering(
896+
graph,
897+
weights,
898+
mutex_uvs,
899+
mutex_weights,
900+
)
901+
```
902+
903+
Notes:
904+
905+
- The attractive edges are the edges of the base graph; the count and the
906+
ordering of `weights` must match `graph.number_of_edges`. Mutex edges
907+
are supplied separately as `(M, 2)` `uint64` pairs with matching
908+
`mutex_weights`.
909+
- Both `weights` and `mutex_weights` accept `float32` and `float64`. The
910+
wrapper dispatches to a templated C++ instantiation per dtype; other
911+
floating dtypes are cast to `float32`. If the two arrays' dtypes do not
912+
match, both are promoted to `float64` rather than silently downcast.
913+
- Higher weights are processed first (in descending order) — the same
914+
convention affogato uses.
915+
- The implementation reuses the union-find and per-root mutex-set helpers
916+
shared with the grid-based mutex watershed (`detail/mutex_storage.hxx`),
917+
so behavior is consistent between the two entry points.
918+
- Output labels are dense `uint64` ids in `0 .. number_of_clusters - 1`,
919+
assigned in first-occurrence order (matches the convention of the graph
920+
multicut solvers, *not* the 1-based foreground labels produced by the
921+
grid-based variant).
922+
- The function accepts both `UndirectedGraph` and `RegionAdjacencyGraph`.
923+
- Tie-breaking is deterministic: when weights are equal, attractive edges
924+
are processed before mutex edges, then by index. Affogato's reference
925+
uses a non-stable `std::sort`, so on inputs with many ties the two
926+
implementations may produce slightly different (but very similar)
927+
partitions. See `development/graph/check_mutex_clustering.py` for a
928+
comparison harness.
929+
856930
## Dictionary-Based Relabeling
857931

858932
If you used a small helper to apply a dictionary to an integer label array, use

0 commit comments

Comments
 (0)