@@ -19,6 +19,22 @@ Graph functionality is under `bic.graph`, segmentation functionality is under
1919` bic.segmentation ` , and utility functionality (blocking, relabeling, overlap
2020measurement, union-find, etc.) is under ` bic.utils ` .
2121
22+ ` bic.graph ` keeps the core graph types and graph-level algorithms
23+ (` UndirectedGraph ` , ` GridGraph2D ` , ` GridGraph3D ` , ` RegionAdjacencyGraph ` ,
24+ ` connected_components ` , ` breadth_first_search ` , ` edge_weighted_watershed ` ,
25+ ` region_adjacency_graph ` , ` project_node_labels_to_pixels ` ) at the top level.
26+ Algorithmic domains live in dedicated submodules:
27+
28+ - ` bic.graph.multicut ` — multicut objective and solvers, fusion-move
29+ proposal generators, multicut problem loaders.
30+ - ` bic.graph.lifted_multicut ` — lifted multicut objective and solvers,
31+ lifted multicut problem loaders. Proposal generators are re-exported
32+ from ` bic.graph.multicut ` here for convenience.
33+ - ` bic.graph.mutex_watershed ` — graph-based mutex watershed clustering
34+ (with and without semantic constraints).
35+ - ` bic.graph.features ` — edge-feature accumulation for RAGs and grid
36+ graphs (boundary maps, affinity channels, lifted edge features).
37+
2238## Affogato
2339
2440### Affinities
@@ -142,7 +158,7 @@ Important migration notes:
142158
143159For mutex watershed on an arbitrary undirected graph (region adjacency graph
144160or otherwise) with a separate list of long-range repulsive edges,
145- ` bioimage-cpp ` provides ` bic.graph.mutex_watershed_clustering ` . This is a
161+ ` bioimage-cpp ` provides ` bic.graph.mutex_watershed. mutex_watershed_clustering ` . This is a
146162port of affogato's ` compute_mws_clustering ` using the same input format as
147163` LiftedMulticutObjective ` : a base graph carries the attractive edges, and
148164long-range (called * mutex* here) edges are supplied alongside as a ` (M, 2) `
@@ -170,7 +186,7 @@ bioimage-cpp:
170186import bioimage_cpp as bic
171187
172188graph = bic.graph.UndirectedGraph.from_edges(number_of_nodes, uvs)
173- labels = bic.graph.mutex_watershed_clustering(
189+ labels = bic.graph.mutex_watershed. mutex_watershed_clustering(
174190 graph,
175191 weights,
176192 mutex_uvs,
@@ -292,7 +308,7 @@ bioimage-cpp:
292308import bioimage_cpp as bic
293309
294310graph = bic.graph.UndirectedGraph.from_edges(number_of_nodes, uvs)
295- labels, semantic_labels = bic.graph.semantic_mutex_watershed_clustering(
311+ labels, semantic_labels = bic.graph.mutex_watershed. semantic_mutex_watershed_clustering(
296312 graph,
297313 weights,
298314 mutex_uvs,
@@ -806,11 +822,11 @@ Multicut problems (3 samples × 2 sizes, originally from
806822
807823``` python
808824# Returns (UndirectedGraph, edge_costs)
809- graph, costs = bic.graph.load_multicut_problem(sample = " A" , size = " small" )
825+ graph, costs = bic.graph.multicut. load_multicut_problem(sample = " A" , size = " small" )
810826# Or just the underlying arrays
811- uv_ids, costs = bic.graph.load_multicut_problem_data(sample = " B" , size = " medium" )
827+ uv_ids, costs = bic.graph.multicut. load_multicut_problem_data(sample = " B" , size = " medium" )
812828# Or the cached file path
813- path = bic.graph.multicut_problem_path(sample = " C" , size = " medium" )
829+ path = bic.graph.multicut. multicut_problem_path(sample = " C" , size = " medium" )
814830```
815831
816832Valid samples are ` "A" ` , ` "B" ` , ` "C" ` ; valid sizes are ` "small" ` and
@@ -824,10 +840,10 @@ Lifted multicut problems (2D ISBI slice, RAG-based 3D volume, and grid-graph
824840volume):
825841
826842``` python
827- problem = bic.graph.load_lifted_multicut_problem(size = " 2d" )
843+ problem = bic.graph.lifted_multicut. load_lifted_multicut_problem(size = " 2d" )
828844# Fields: n_nodes (int), local_uvs, local_costs, lifted_uvs, lifted_costs.
829845graph = bic.graph.UndirectedGraph.from_edges(problem.n_nodes, problem.local_uvs)
830- objective = bic.graph.LiftedMulticutObjective(
846+ objective = bic.graph.lifted_multicut. LiftedMulticutObjective(
831847 graph,
832848 problem.local_costs,
833849 lifted_uvs = problem.lifted_uvs,
@@ -914,14 +930,14 @@ bioimage-cpp:
914930``` python
915931import bioimage_cpp as bic
916932
917- objective = bic.graph.LiftedMulticutObjective(
933+ objective = bic.graph.lifted_multicut. LiftedMulticutObjective(
918934 graph,
919935 edge_costs,
920936 lifted_uvs = lifted_uvs,
921937 lifted_costs = lifted_costs,
922938 bfs_distance = 3 , # optional: also insert zero-weight lifted edges within k hops
923939)
924- labels = bic.graph.LiftedGreedyAdditiveMulticut().optimize(objective)
940+ labels = bic.graph.lifted_multicut. LiftedGreedyAdditiveMulticut().optimize(objective)
925941energy = objective.energy(labels)
926942```
927943
@@ -969,11 +985,11 @@ safety net). The differences are:
969985 pluggable via ` sub_solver= ` .
970986
971987``` python
972- solver = bic.graph.FusionMoveLiftedMulticut(
973- proposal_generator = bic.graph.WatershedProposalGenerator(
988+ solver = bic.graph.lifted_multicut. FusionMoveLiftedMulticut(
989+ proposal_generator = bic.graph.lifted_multicut. WatershedProposalGenerator(
974990 sigma = 1.0 , n_seeds_fraction = 0.1 , seed = 0 ,
975991 ),
976- sub_solver = bic.graph.LiftedKernighanLinMulticut(number_of_outer_iterations = 3 ),
992+ sub_solver = bic.graph.lifted_multicut. LiftedKernighanLinMulticut(number_of_outer_iterations = 3 ),
977993 number_of_iterations = 10 ,
978994 stop_if_no_improvement = 4 ,
979995 number_of_threads = 4 ,
@@ -984,9 +1000,9 @@ labels = solver.optimize(objective)
9841000A typical warm-started solve combines greedy and KL:
9851001
9861002``` python
987- solver = bic.graph.LiftedChainedSolvers([
988- bic.graph.LiftedGreedyAdditiveMulticut(),
989- bic.graph.LiftedKernighanLinMulticut(number_of_outer_iterations = 10 ),
1003+ solver = bic.graph.lifted_multicut. LiftedChainedSolvers([
1004+ bic.graph.lifted_multicut. LiftedGreedyAdditiveMulticut(),
1005+ bic.graph.lifted_multicut. LiftedKernighanLinMulticut(number_of_outer_iterations = 10 ),
9901006])
9911007labels = solver.optimize(objective)
9921008```
@@ -1016,19 +1032,19 @@ two focused helpers that cover the same workflow:
10161032``` python
10171033# Discover lifted edges implied by long-range affinity offsets. 1-hop offsets
10181034# are skipped automatically, so the full offset list can be passed in.
1019- lifted_uvs = bic.graph.lifted_edges_from_affinities(
1035+ lifted_uvs = bic.graph.features. lifted_edges_from_affinities(
10201036 rag, oversegmentation, offsets, number_of_threads = 0 ,
10211037)
10221038
10231039# Accumulate (mean, size) statistics per lifted edge. Pixel pairs whose
10241040# (u, v) does not appear in `lifted_uvs` are skipped, so local edges are
10251041# never contaminated with long-range affinities.
1026- lifted_features = bic.graph.lifted_affinity_features(
1042+ lifted_features = bic.graph.features. lifted_affinity_features(
10271043 oversegmentation, affinities, offsets, lifted_uvs,
10281044 number_of_threads = 0 ,
10291045)
10301046# For the 12-column feature set (mean, median, std, min, max, percentiles, size):
1031- lifted_features = bic.graph.lifted_affinity_features_complex(... )
1047+ lifted_features = bic.graph.features. lifted_affinity_features_complex(... )
10321048```
10331049
10341050The output column conventions match the local-edge variants
@@ -1038,16 +1054,16 @@ End-to-end pipeline (also in `examples/segmentation/lifted_multicut_from_affinit
10381054
10391055``` python
10401056rag = bic.graph.region_adjacency_graph(oversegmentation)
1041- local_costs = local_threshold - bic.graph.affinity_features(
1057+ local_costs = local_threshold - bic.graph.features. affinity_features(
10421058 rag, oversegmentation, direct_affinities, direct_offsets,
10431059)[:, 0 ]
1044- lifted_uvs = bic.graph.lifted_edges_from_affinities(
1060+ lifted_uvs = bic.graph.features. lifted_edges_from_affinities(
10451061 rag, oversegmentation, long_range_offsets,
10461062)
1047- lifted_costs = lifted_threshold - bic.graph.lifted_affinity_features(
1063+ lifted_costs = lifted_threshold - bic.graph.features. lifted_affinity_features(
10481064 oversegmentation, long_range_affinities, long_range_offsets, lifted_uvs,
10491065)[:, 0 ]
1050- objective = bic.graph.LiftedMulticutObjective(
1066+ objective = bic.graph.lifted_multicut. LiftedMulticutObjective(
10511067 rag, local_costs, lifted_uvs = lifted_uvs, lifted_costs = lifted_costs,
10521068)
10531069```
@@ -1074,8 +1090,8 @@ bioimage-cpp:
10741090``` python
10751091import bioimage_cpp as bic
10761092
1077- objective = bic.graph.MulticutObjective(graph, edge_costs)
1078- labels = bic.graph.GreedyAdditiveMulticut().optimize(objective)
1093+ objective = bic.graph.multicut. MulticutObjective(graph, edge_costs)
1094+ labels = bic.graph.multicut. GreedyAdditiveMulticut().optimize(objective)
10791095energy = objective.energy(labels)
10801096```
10811097
@@ -1111,7 +1127,7 @@ Constructor argument mapping:
11111127Kernighan-Lin example:
11121128
11131129``` python
1114- solver = bic.graph.KernighanLinMulticut(number_of_outer_iterations = 5 )
1130+ solver = bic.graph.multicut. KernighanLinMulticut(number_of_outer_iterations = 5 )
11151131labels = solver.optimize(objective)
11161132```
11171133
@@ -1123,9 +1139,9 @@ warm-start, set `objective.set_labels(...)` to a non-trivial labeling first.
11231139Chaining solvers:
11241140
11251141``` python
1126- solver = bic.graph.ChainedMulticutSolvers([
1127- bic.graph.GreedyAdditiveMulticut(),
1128- bic.graph.KernighanLinMulticut(number_of_outer_iterations = 5 ),
1142+ solver = bic.graph.multicut. ChainedMulticutSolvers([
1143+ bic.graph.multicut. GreedyAdditiveMulticut(),
1144+ bic.graph.multicut. KernighanLinMulticut(number_of_outer_iterations = 5 ),
11291145])
11301146labels = solver.optimize(objective)
11311147```
@@ -1134,9 +1150,9 @@ Decomposing a problem into positive-cost connected components and solving each
11341150sub-problem with a cheaper solver:
11351151
11361152``` python
1137- solver = bic.graph.MulticutDecomposer(
1138- sub_solver = bic.graph.KernighanLinMulticut(number_of_outer_iterations = 5 ),
1139- fallthrough_solver = bic.graph.GreedyAdditiveMulticut(),
1153+ solver = bic.graph.multicut. MulticutDecomposer(
1154+ sub_solver = bic.graph.multicut. KernighanLinMulticut(number_of_outer_iterations = 5 ),
1155+ fallthrough_solver = bic.graph.multicut. GreedyAdditiveMulticut(),
11401156 number_of_threads = 0 ,
11411157)
11421158labels = solver.optimize(objective)
@@ -1190,12 +1206,12 @@ bioimage-cpp:
11901206``` python
11911207import bioimage_cpp as bic
11921208
1193- objective = bic.graph.MulticutObjective(graph, edge_costs)
1194- solver = bic.graph.FusionMoveMulticut(
1195- proposal_generator = bic.graph.WatershedProposalGenerator(
1209+ objective = bic.graph.multicut. MulticutObjective(graph, edge_costs)
1210+ solver = bic.graph.multicut. FusionMoveMulticut(
1211+ proposal_generator = bic.graph.multicut. WatershedProposalGenerator(
11961212 sigma = 1.0 , n_seeds_fraction = 0.1 , seed = 0 ,
11971213 ),
1198- sub_solver = bic.graph.GreedyAdditiveMulticut(),
1214+ sub_solver = bic.graph.multicut. GreedyAdditiveMulticut(),
11991215 number_of_iterations = 10 ,
12001216 stop_if_no_improvement = 4 ,
12011217)
@@ -1283,34 +1299,34 @@ Simple edge-map features:
12831299
12841300``` python
12851301rag = bic.graph.region_adjacency_graph(labels)
1286- features = bic.graph.edge_map_features(rag, labels, edge_map)
1302+ features = bic.graph.features. edge_map_features(rag, labels, edge_map)
12871303```
12881304
12891305The columns are:
12901306
12911307``` python
1292- bic.graph.SIMPLE_EDGE_FEATURE_NAMES
1308+ bic.graph.features. SIMPLE_EDGE_FEATURE_NAMES
12931309# ("mean", "size")
12941310```
12951311
12961312Complex edge-map features:
12971313
12981314``` python
1299- features = bic.graph.edge_map_features_complex(rag, labels, edge_map)
1315+ features = bic.graph.features. edge_map_features_complex(rag, labels, edge_map)
13001316```
13011317
13021318The columns are:
13031319
13041320``` python
1305- bic.graph.COMPLEX_EDGE_FEATURE_NAMES
1321+ bic.graph.features. COMPLEX_EDGE_FEATURE_NAMES
13061322# ("mean", "median", "std", "min", "max", "p5", "p10",
13071323# "p25", "p75", "p90", "p95", "size")
13081324```
13091325
13101326Affinity features:
13111327
13121328``` python
1313- features = bic.graph.affinity_features(
1329+ features = bic.graph.features. affinity_features(
13141330 rag,
13151331 labels,
13161332 affinities,
@@ -1321,7 +1337,7 @@ features = bic.graph.affinity_features(
13211337Complex affinity features:
13221338
13231339``` python
1324- features = bic.graph.affinity_features_complex(
1340+ features = bic.graph.features. affinity_features_complex(
13251341 rag,
13261342 labels,
13271343 affinities,
0 commit comments