copick-utils 0.6.0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- copick_utils/__init__.py +1 -0
- copick_utils/cli/__init__.py +33 -0
- copick_utils/cli/clipmesh.py +161 -0
- copick_utils/cli/clippicks.py +154 -0
- copick_utils/cli/clipseg.py +163 -0
- copick_utils/cli/conversion_commands.py +32 -0
- copick_utils/cli/enclosed.py +191 -0
- copick_utils/cli/filter_components.py +166 -0
- copick_utils/cli/fit_spline.py +191 -0
- copick_utils/cli/hull.py +138 -0
- copick_utils/cli/input_output_selection.py +76 -0
- copick_utils/cli/logical_commands.py +29 -0
- copick_utils/cli/mesh2picks.py +170 -0
- copick_utils/cli/mesh2seg.py +167 -0
- copick_utils/cli/meshop.py +262 -0
- copick_utils/cli/picks2ellipsoid.py +171 -0
- copick_utils/cli/picks2mesh.py +181 -0
- copick_utils/cli/picks2plane.py +156 -0
- copick_utils/cli/picks2seg.py +134 -0
- copick_utils/cli/picks2sphere.py +170 -0
- copick_utils/cli/picks2surface.py +164 -0
- copick_utils/cli/picksin.py +146 -0
- copick_utils/cli/picksout.py +148 -0
- copick_utils/cli/processing_commands.py +18 -0
- copick_utils/cli/seg2mesh.py +135 -0
- copick_utils/cli/seg2picks.py +128 -0
- copick_utils/cli/segop.py +248 -0
- copick_utils/cli/separate_components.py +155 -0
- copick_utils/cli/skeletonize.py +164 -0
- copick_utils/cli/util.py +580 -0
- copick_utils/cli/validbox.py +155 -0
- copick_utils/converters/__init__.py +35 -0
- copick_utils/converters/converter_common.py +543 -0
- copick_utils/converters/ellipsoid_from_picks.py +335 -0
- copick_utils/converters/lazy_converter.py +576 -0
- copick_utils/converters/mesh_from_picks.py +209 -0
- copick_utils/converters/mesh_from_segmentation.py +119 -0
- copick_utils/converters/picks_from_mesh.py +542 -0
- copick_utils/converters/picks_from_segmentation.py +168 -0
- copick_utils/converters/plane_from_picks.py +251 -0
- copick_utils/converters/segmentation_from_mesh.py +291 -0
- copick_utils/{segmentation → converters}/segmentation_from_picks.py +151 -15
- copick_utils/converters/sphere_from_picks.py +306 -0
- copick_utils/converters/surface_from_picks.py +337 -0
- copick_utils/features/skimage.py +33 -13
- copick_utils/io/readers.py +62 -59
- copick_utils/io/writers.py +9 -14
- copick_utils/logical/__init__.py +43 -0
- copick_utils/logical/distance_operations.py +604 -0
- copick_utils/logical/enclosed_operations.py +222 -0
- copick_utils/logical/mesh_operations.py +443 -0
- copick_utils/logical/point_operations.py +303 -0
- copick_utils/logical/segmentation_operations.py +399 -0
- copick_utils/pickers/grid_picker.py +5 -4
- copick_utils/process/__init__.py +47 -0
- copick_utils/process/connected_components.py +360 -0
- copick_utils/process/filter_components.py +306 -0
- copick_utils/process/hull.py +106 -0
- copick_utils/process/skeletonize.py +326 -0
- copick_utils/process/spline_fitting.py +648 -0
- copick_utils/process/validbox.py +333 -0
- copick_utils/util/__init__.py +6 -0
- copick_utils/util/config_models.py +614 -0
- {copick_utils-0.6.0.dist-info → copick_utils-1.0.0.dist-info}/METADATA +38 -12
- copick_utils-1.0.0.dist-info/RECORD +71 -0
- {copick_utils-0.6.0.dist-info → copick_utils-1.0.0.dist-info}/WHEEL +1 -1
- copick_utils-1.0.0.dist-info/entry_points.txt +29 -0
- copick_utils/__about__.py +0 -4
- copick_utils/segmentation/picks_from_segmentation.py +0 -67
- copick_utils-0.6.0.dist-info/RECORD +0 -15
- /copick_utils/{segmentation → io}/__init__.py +0 -0
- /copick_utils-0.6.0.dist-info/LICENSE.txt → /copick_utils-1.0.0.dist-info/licenses/LICENSE +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import copick
|
|
3
|
+
from click_option_group import optgroup
|
|
4
|
+
from copick.cli.util import add_config_option, add_debug_option
|
|
5
|
+
from copick.util.log import get_logger
|
|
6
|
+
from copick.util.uri import parse_copick_uri
|
|
7
|
+
|
|
8
|
+
from copick_utils.cli.util import (
|
|
9
|
+
add_input_option,
|
|
10
|
+
add_output_option,
|
|
11
|
+
add_reference_mesh_option,
|
|
12
|
+
add_reference_seg_option,
|
|
13
|
+
add_workers_option,
|
|
14
|
+
)
|
|
15
|
+
from copick_utils.util.config_models import create_reference_config
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.command(
|
|
19
|
+
context_settings={"show_default": True},
|
|
20
|
+
short_help="Filter picks to include only those inside a reference volume.",
|
|
21
|
+
no_args_is_help=True,
|
|
22
|
+
)
|
|
23
|
+
@add_config_option
|
|
24
|
+
@optgroup.group("\nInput Options", help="Options related to the input picks.")
|
|
25
|
+
@optgroup.option(
|
|
26
|
+
"--run-names",
|
|
27
|
+
"-r",
|
|
28
|
+
multiple=True,
|
|
29
|
+
help="Specific run names to process (default: all runs).",
|
|
30
|
+
)
|
|
31
|
+
@add_input_option("picks")
|
|
32
|
+
@optgroup.group("\nReference Options", help="Options for reference volume (provide either mesh or segmentation).")
|
|
33
|
+
@add_reference_mesh_option(required=False)
|
|
34
|
+
@add_reference_seg_option(required=False)
|
|
35
|
+
@optgroup.group("\nTool Options", help="Options related to this tool.")
|
|
36
|
+
@add_workers_option
|
|
37
|
+
@optgroup.group("\nOutput Options", help="Options related to output picks.")
|
|
38
|
+
@add_output_option("picks", default_tool="picksin")
|
|
39
|
+
@add_debug_option
|
|
40
|
+
def picksin(
|
|
41
|
+
config,
|
|
42
|
+
run_names,
|
|
43
|
+
input_uri,
|
|
44
|
+
ref_mesh_uri,
|
|
45
|
+
ref_seg_uri,
|
|
46
|
+
workers,
|
|
47
|
+
output_uri,
|
|
48
|
+
debug,
|
|
49
|
+
):
|
|
50
|
+
"""
|
|
51
|
+
Filter picks to include only those inside a reference volume.
|
|
52
|
+
|
|
53
|
+
\b
|
|
54
|
+
URI Format:
|
|
55
|
+
Picks: object_name:user_id/session_id
|
|
56
|
+
Meshes: object_name:user_id/session_id
|
|
57
|
+
Segmentations: name:user_id/session_id@voxel_spacing
|
|
58
|
+
|
|
59
|
+
\b
|
|
60
|
+
The reference volume can be either a watertight mesh or a segmentation.
|
|
61
|
+
Only picks that fall inside the reference volume will be kept.
|
|
62
|
+
|
|
63
|
+
\b
|
|
64
|
+
Examples:
|
|
65
|
+
# Include only picks inside reference mesh
|
|
66
|
+
copick logical picksin -i "ribosome:user1/all-001" -rm "boundary:user1/boundary-001" -o "ribosome:picksin/inside-001"
|
|
67
|
+
|
|
68
|
+
# Include only picks inside segmentation
|
|
69
|
+
copick logical picksin -i "ribosome:user1/all-001" -rs "mask:user1/mask-001@10.0" -o "ribosome:picksin/inside-001"
|
|
70
|
+
"""
|
|
71
|
+
from copick_utils.logical.point_operations import picks_inclusion_by_mesh_lazy_batch
|
|
72
|
+
|
|
73
|
+
logger = get_logger(__name__, debug=debug)
|
|
74
|
+
|
|
75
|
+
# Validate that exactly one reference type is provided
|
|
76
|
+
if not ref_mesh_uri and not ref_seg_uri:
|
|
77
|
+
raise click.BadParameter("Must provide either --ref-mesh or --ref-seg")
|
|
78
|
+
if ref_mesh_uri and ref_seg_uri:
|
|
79
|
+
raise click.BadParameter("Cannot provide both --ref-mesh and --ref-seg")
|
|
80
|
+
|
|
81
|
+
root = copick.from_file(config)
|
|
82
|
+
run_names_list = list(run_names) if run_names else None
|
|
83
|
+
|
|
84
|
+
# Determine reference type and URI
|
|
85
|
+
reference_uri = ref_mesh_uri or ref_seg_uri
|
|
86
|
+
reference_type = "mesh" if ref_mesh_uri else "segmentation"
|
|
87
|
+
|
|
88
|
+
# Create config directly from URIs with smart defaults
|
|
89
|
+
try:
|
|
90
|
+
task_config = create_reference_config(
|
|
91
|
+
input_uri=input_uri,
|
|
92
|
+
input_type="picks",
|
|
93
|
+
output_uri=output_uri,
|
|
94
|
+
output_type="picks",
|
|
95
|
+
reference_uri=reference_uri,
|
|
96
|
+
reference_type=reference_type,
|
|
97
|
+
command_name="picksin",
|
|
98
|
+
)
|
|
99
|
+
except ValueError as e:
|
|
100
|
+
raise click.BadParameter(str(e)) from e
|
|
101
|
+
|
|
102
|
+
# Extract parameters for logging
|
|
103
|
+
input_params = parse_copick_uri(input_uri, "picks")
|
|
104
|
+
output_params = parse_copick_uri(output_uri, "picks")
|
|
105
|
+
ref_params = parse_copick_uri(reference_uri, reference_type)
|
|
106
|
+
|
|
107
|
+
logger.info(f"Including picks inside reference volume for object '{input_params['object_name']}'")
|
|
108
|
+
logger.info(f"Source picks pattern: {input_params['user_id']}/{input_params['session_id']}")
|
|
109
|
+
if reference_type == "mesh":
|
|
110
|
+
logger.info(f"Reference mesh: {ref_params['object_name']} ({ref_params['user_id']}/{ref_params['session_id']})")
|
|
111
|
+
else:
|
|
112
|
+
logger.info(
|
|
113
|
+
f"Reference segmentation: {ref_params['name']} ({ref_params['user_id']}/{ref_params['session_id']})",
|
|
114
|
+
)
|
|
115
|
+
logger.info(
|
|
116
|
+
f"Target picks template: {output_params['object_name']} ({output_params['user_id']}/{output_params['session_id']})",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Parallel discovery and processing - no sequential bottleneck!
|
|
120
|
+
results = picks_inclusion_by_mesh_lazy_batch(
|
|
121
|
+
root=root,
|
|
122
|
+
config=task_config,
|
|
123
|
+
run_names=run_names_list,
|
|
124
|
+
workers=workers,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
|
|
128
|
+
total_points = sum(result.get("points_created", 0) for result in results.values() if result)
|
|
129
|
+
total_processed = sum(result.get("processed", 0) for result in results.values() if result)
|
|
130
|
+
|
|
131
|
+
# Collect all errors
|
|
132
|
+
all_errors = []
|
|
133
|
+
for result in results.values():
|
|
134
|
+
if result and result.get("errors"):
|
|
135
|
+
all_errors.extend(result["errors"])
|
|
136
|
+
|
|
137
|
+
logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
|
|
138
|
+
logger.info(f"Total inclusion operations completed: {total_processed}")
|
|
139
|
+
logger.info(f"Total points included: {total_points}")
|
|
140
|
+
|
|
141
|
+
if all_errors:
|
|
142
|
+
logger.warning(f"Encountered {len(all_errors)} errors during processing")
|
|
143
|
+
for error in all_errors[:5]: # Show first 5 errors
|
|
144
|
+
logger.warning(f" - {error}")
|
|
145
|
+
if len(all_errors) > 5:
|
|
146
|
+
logger.warning(f" ... and {len(all_errors) - 5} more errors")
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""CLI commands for point filtering operations (inclusion/exclusion by volume)."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import copick
|
|
5
|
+
from click_option_group import optgroup
|
|
6
|
+
from copick.cli.util import add_config_option, add_debug_option
|
|
7
|
+
from copick.util.log import get_logger
|
|
8
|
+
from copick.util.uri import parse_copick_uri
|
|
9
|
+
|
|
10
|
+
from copick_utils.cli.util import (
|
|
11
|
+
add_input_option,
|
|
12
|
+
add_output_option,
|
|
13
|
+
add_reference_mesh_option,
|
|
14
|
+
add_reference_seg_option,
|
|
15
|
+
add_workers_option,
|
|
16
|
+
)
|
|
17
|
+
from copick_utils.util.config_models import create_reference_config
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command(
|
|
21
|
+
context_settings={"show_default": True},
|
|
22
|
+
short_help="Filter picks to exclude those inside a reference volume.",
|
|
23
|
+
no_args_is_help=True,
|
|
24
|
+
)
|
|
25
|
+
@add_config_option
|
|
26
|
+
@optgroup.group("\nInput Options", help="Options related to the input picks.")
|
|
27
|
+
@optgroup.option(
|
|
28
|
+
"--run-names",
|
|
29
|
+
"-r",
|
|
30
|
+
multiple=True,
|
|
31
|
+
help="Specific run names to process (default: all runs).",
|
|
32
|
+
)
|
|
33
|
+
@add_input_option("picks")
|
|
34
|
+
@optgroup.group("\nReference Options", help="Options for reference volume (provide either mesh or segmentation).")
|
|
35
|
+
@add_reference_mesh_option(required=False)
|
|
36
|
+
@add_reference_seg_option(required=False)
|
|
37
|
+
@optgroup.group("\nTool Options", help="Options related to this tool.")
|
|
38
|
+
@add_workers_option
|
|
39
|
+
@optgroup.group("\nOutput Options", help="Options related to output picks.")
|
|
40
|
+
@add_output_option("picks", default_tool="picksout")
|
|
41
|
+
@add_debug_option
|
|
42
|
+
def picksout(
|
|
43
|
+
config,
|
|
44
|
+
run_names,
|
|
45
|
+
input_uri,
|
|
46
|
+
ref_mesh_uri,
|
|
47
|
+
ref_seg_uri,
|
|
48
|
+
workers,
|
|
49
|
+
output_uri,
|
|
50
|
+
debug,
|
|
51
|
+
):
|
|
52
|
+
"""
|
|
53
|
+
Filter picks to exclude those inside a reference volume.
|
|
54
|
+
|
|
55
|
+
\b
|
|
56
|
+
URI Format:
|
|
57
|
+
Picks: object_name:user_id/session_id
|
|
58
|
+
Meshes: object_name:user_id/session_id
|
|
59
|
+
Segmentations: name:user_id/session_id@voxel_spacing
|
|
60
|
+
|
|
61
|
+
\b
|
|
62
|
+
The reference volume can be either a watertight mesh or a segmentation.
|
|
63
|
+
Picks that fall inside the reference volume will be removed.
|
|
64
|
+
|
|
65
|
+
\b
|
|
66
|
+
Examples:
|
|
67
|
+
# Exclude picks inside reference mesh
|
|
68
|
+
copick logical picksout -i "ribosome:user1/all-001" -rm "boundary:user1/boundary-001" -o "ribosome:picksout/outside-001"
|
|
69
|
+
|
|
70
|
+
# Exclude picks inside segmentation
|
|
71
|
+
copick logical picksout -i "ribosome:user1/all-001" -rs "mask:user1/mask-001@10.0" -o "ribosome:picksout/outside-001"
|
|
72
|
+
"""
|
|
73
|
+
from copick_utils.logical.point_operations import picks_exclusion_by_mesh_lazy_batch
|
|
74
|
+
|
|
75
|
+
logger = get_logger(__name__, debug=debug)
|
|
76
|
+
|
|
77
|
+
# Validate that exactly one reference type is provided
|
|
78
|
+
if not ref_mesh_uri and not ref_seg_uri:
|
|
79
|
+
raise click.BadParameter("Must provide either --ref-mesh or --ref-seg")
|
|
80
|
+
if ref_mesh_uri and ref_seg_uri:
|
|
81
|
+
raise click.BadParameter("Cannot provide both --ref-mesh and --ref-seg")
|
|
82
|
+
|
|
83
|
+
root = copick.from_file(config)
|
|
84
|
+
run_names_list = list(run_names) if run_names else None
|
|
85
|
+
|
|
86
|
+
# Determine reference type and URI
|
|
87
|
+
reference_uri = ref_mesh_uri or ref_seg_uri
|
|
88
|
+
reference_type = "mesh" if ref_mesh_uri else "segmentation"
|
|
89
|
+
|
|
90
|
+
# Create config directly from URIs with smart defaults
|
|
91
|
+
try:
|
|
92
|
+
task_config = create_reference_config(
|
|
93
|
+
input_uri=input_uri,
|
|
94
|
+
input_type="picks",
|
|
95
|
+
output_uri=output_uri,
|
|
96
|
+
output_type="picks",
|
|
97
|
+
reference_uri=reference_uri,
|
|
98
|
+
reference_type=reference_type,
|
|
99
|
+
command_name="picksout",
|
|
100
|
+
)
|
|
101
|
+
except ValueError as e:
|
|
102
|
+
raise click.BadParameter(str(e)) from e
|
|
103
|
+
|
|
104
|
+
# Extract parameters for logging
|
|
105
|
+
input_params = parse_copick_uri(input_uri, "picks")
|
|
106
|
+
output_params = parse_copick_uri(output_uri, "picks")
|
|
107
|
+
ref_params = parse_copick_uri(reference_uri, reference_type)
|
|
108
|
+
|
|
109
|
+
logger.info(f"Excluding picks inside reference volume for object '{input_params['object_name']}'")
|
|
110
|
+
logger.info(f"Source picks pattern: {input_params['user_id']}/{input_params['session_id']}")
|
|
111
|
+
if reference_type == "mesh":
|
|
112
|
+
logger.info(f"Reference mesh: {ref_params['object_name']} ({ref_params['user_id']}/{ref_params['session_id']})")
|
|
113
|
+
else:
|
|
114
|
+
logger.info(
|
|
115
|
+
f"Reference segmentation: {ref_params['name']} ({ref_params['user_id']}/{ref_params['session_id']})",
|
|
116
|
+
)
|
|
117
|
+
logger.info(
|
|
118
|
+
f"Target picks template: {output_params['object_name']} ({output_params['user_id']}/{output_params['session_id']})",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Parallel discovery and processing - no sequential bottleneck!
|
|
122
|
+
results = picks_exclusion_by_mesh_lazy_batch(
|
|
123
|
+
root=root,
|
|
124
|
+
config=task_config,
|
|
125
|
+
run_names=run_names_list,
|
|
126
|
+
workers=workers,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
|
|
130
|
+
total_points = sum(result.get("points_created", 0) for result in results.values() if result)
|
|
131
|
+
total_processed = sum(result.get("processed", 0) for result in results.values() if result)
|
|
132
|
+
|
|
133
|
+
# Collect all errors
|
|
134
|
+
all_errors = []
|
|
135
|
+
for result in results.values():
|
|
136
|
+
if result and result.get("errors"):
|
|
137
|
+
all_errors.extend(result["errors"])
|
|
138
|
+
|
|
139
|
+
logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
|
|
140
|
+
logger.info(f"Total exclusion operations completed: {total_processed}")
|
|
141
|
+
logger.info(f"Total points excluded (remaining): {total_points}")
|
|
142
|
+
|
|
143
|
+
if all_errors:
|
|
144
|
+
logger.warning(f"Encountered {len(all_errors)} errors during processing")
|
|
145
|
+
for error in all_errors[:5]: # Show first 5 errors
|
|
146
|
+
logger.warning(f" - {error}")
|
|
147
|
+
if len(all_errors) > 5:
|
|
148
|
+
logger.warning(f" ... and {len(all_errors) - 5} more errors")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""CLI commands for segmentation processing operations."""
|
|
2
|
+
|
|
3
|
+
from copick_utils.cli.filter_components import filter_components
|
|
4
|
+
from copick_utils.cli.fit_spline import fit_spline
|
|
5
|
+
from copick_utils.cli.hull import hull
|
|
6
|
+
from copick_utils.cli.separate_components import separate_components
|
|
7
|
+
from copick_utils.cli.skeletonize import skeletonize
|
|
8
|
+
from copick_utils.cli.validbox import validbox
|
|
9
|
+
|
|
10
|
+
# All commands are now available for import by the main CLI
|
|
11
|
+
__all__ = [
|
|
12
|
+
"validbox",
|
|
13
|
+
"hull",
|
|
14
|
+
"skeletonize",
|
|
15
|
+
"separate_components",
|
|
16
|
+
"filter_components",
|
|
17
|
+
"fit_spline",
|
|
18
|
+
]
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import copick
|
|
3
|
+
from click_option_group import optgroup
|
|
4
|
+
from copick.cli.util import add_config_option, add_debug_option
|
|
5
|
+
from copick.util.log import get_logger
|
|
6
|
+
from copick.util.uri import parse_copick_uri
|
|
7
|
+
|
|
8
|
+
from copick_utils.cli.util import (
|
|
9
|
+
add_input_option,
|
|
10
|
+
add_marching_cubes_options,
|
|
11
|
+
add_output_option,
|
|
12
|
+
add_workers_option,
|
|
13
|
+
)
|
|
14
|
+
from copick_utils.util.config_models import create_simple_config
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.command(
|
|
18
|
+
context_settings={"show_default": True},
|
|
19
|
+
short_help="Convert segmentation to mesh.",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
@add_config_option
|
|
23
|
+
@optgroup.group("\nInput Options", help="Options related to the input segmentations.")
|
|
24
|
+
@optgroup.option(
|
|
25
|
+
"--run-names",
|
|
26
|
+
"-r",
|
|
27
|
+
multiple=True,
|
|
28
|
+
help="Specific run names to process (default: all runs).",
|
|
29
|
+
)
|
|
30
|
+
@add_input_option("segmentation")
|
|
31
|
+
@optgroup.group("\nTool Options", help="Options related to this tool.")
|
|
32
|
+
@add_marching_cubes_options
|
|
33
|
+
@add_workers_option
|
|
34
|
+
@optgroup.group("\nOutput Options", help="Options related to output meshes.")
|
|
35
|
+
@add_output_option("mesh", default_tool="seg2mesh")
|
|
36
|
+
@optgroup.option(
|
|
37
|
+
"--individual-meshes/--no-individual-meshes",
|
|
38
|
+
"-im",
|
|
39
|
+
is_flag=True,
|
|
40
|
+
default=False,
|
|
41
|
+
help="Create individual meshes for each instance (enables {instance_id} placeholder).",
|
|
42
|
+
)
|
|
43
|
+
@add_debug_option
|
|
44
|
+
def seg2mesh(
|
|
45
|
+
config,
|
|
46
|
+
run_names,
|
|
47
|
+
input_uri,
|
|
48
|
+
level,
|
|
49
|
+
step_size,
|
|
50
|
+
workers,
|
|
51
|
+
output_uri,
|
|
52
|
+
individual_meshes,
|
|
53
|
+
debug,
|
|
54
|
+
):
|
|
55
|
+
"""
|
|
56
|
+
Convert segmentation volumes to meshes using marching cubes.
|
|
57
|
+
|
|
58
|
+
\b
|
|
59
|
+
URI Format:
|
|
60
|
+
Segmentations: name:user_id/session_id@voxel_spacing
|
|
61
|
+
Meshes: object_name:user_id/session_id
|
|
62
|
+
|
|
63
|
+
\b
|
|
64
|
+
Examples:
|
|
65
|
+
# Convert single segmentation to mesh
|
|
66
|
+
copick convert seg2mesh -i "membrane:user1/manual-001@10.0" -o "membrane:seg2mesh/from-seg-001"
|
|
67
|
+
|
|
68
|
+
# Convert all manual segmentations using pattern matching
|
|
69
|
+
copick convert seg2mesh -i "membrane:user1/manual-.*@10.0" -o "membrane:seg2mesh/from-seg-{input_session_id}"
|
|
70
|
+
"""
|
|
71
|
+
from copick_utils.converters.mesh_from_segmentation import mesh_from_segmentation_lazy_batch
|
|
72
|
+
|
|
73
|
+
logger = get_logger(__name__, debug=debug)
|
|
74
|
+
|
|
75
|
+
root = copick.from_file(config)
|
|
76
|
+
run_names_list = list(run_names) if run_names else None
|
|
77
|
+
|
|
78
|
+
# Create config directly from URIs with smart defaults
|
|
79
|
+
try:
|
|
80
|
+
task_config = create_simple_config(
|
|
81
|
+
input_uri=input_uri,
|
|
82
|
+
input_type="segmentation",
|
|
83
|
+
output_uri=output_uri,
|
|
84
|
+
output_type="mesh",
|
|
85
|
+
individual_outputs=individual_meshes,
|
|
86
|
+
command_name="seg2mesh",
|
|
87
|
+
)
|
|
88
|
+
except ValueError as e:
|
|
89
|
+
raise click.BadParameter(str(e)) from e
|
|
90
|
+
|
|
91
|
+
# Extract parameters for logging
|
|
92
|
+
input_params = parse_copick_uri(input_uri, "segmentation")
|
|
93
|
+
output_params = parse_copick_uri(output_uri, "mesh")
|
|
94
|
+
|
|
95
|
+
logger.info(f"Converting segmentation to mesh for '{input_params['name']}'")
|
|
96
|
+
logger.info(
|
|
97
|
+
f"Source segmentation pattern: {input_params['name']} ({input_params['user_id']}/{input_params['session_id']})",
|
|
98
|
+
)
|
|
99
|
+
logger.info(
|
|
100
|
+
f"Target mesh template: {output_params['object_name']} ({output_params['user_id']}/{output_params['session_id']})",
|
|
101
|
+
)
|
|
102
|
+
logger.info(f"Marching cubes level: {level}, step size: {step_size}")
|
|
103
|
+
|
|
104
|
+
# Parallel discovery and processing - no sequential bottleneck!
|
|
105
|
+
results = mesh_from_segmentation_lazy_batch(
|
|
106
|
+
root=root,
|
|
107
|
+
config=task_config,
|
|
108
|
+
run_names=run_names_list,
|
|
109
|
+
workers=workers,
|
|
110
|
+
level=level,
|
|
111
|
+
step_size=step_size,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
|
|
115
|
+
total_vertices = sum(result.get("vertices_created", 0) for result in results.values() if result)
|
|
116
|
+
total_faces = sum(result.get("faces_created", 0) for result in results.values() if result)
|
|
117
|
+
total_processed = sum(result.get("processed", 0) for result in results.values() if result)
|
|
118
|
+
|
|
119
|
+
# Collect all errors
|
|
120
|
+
all_errors = []
|
|
121
|
+
for result in results.values():
|
|
122
|
+
if result and result.get("errors"):
|
|
123
|
+
all_errors.extend(result["errors"])
|
|
124
|
+
|
|
125
|
+
logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
|
|
126
|
+
logger.info(f"Total conversion tasks completed: {total_processed}")
|
|
127
|
+
logger.info(f"Total vertices created: {total_vertices}")
|
|
128
|
+
logger.info(f"Total faces created: {total_faces}")
|
|
129
|
+
|
|
130
|
+
if all_errors:
|
|
131
|
+
logger.warning(f"Encountered {len(all_errors)} errors during processing")
|
|
132
|
+
for error in all_errors[:5]: # Show first 5 errors
|
|
133
|
+
logger.warning(f" - {error}")
|
|
134
|
+
if len(all_errors) > 5:
|
|
135
|
+
logger.warning(f" ... and {len(all_errors) - 5} more errors")
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import copick
|
|
3
|
+
from click_option_group import optgroup
|
|
4
|
+
from copick.cli.util import add_config_option, add_debug_option
|
|
5
|
+
from copick.util.log import get_logger
|
|
6
|
+
from copick.util.uri import parse_copick_uri
|
|
7
|
+
|
|
8
|
+
from copick_utils.cli.util import (
|
|
9
|
+
add_input_option,
|
|
10
|
+
add_output_option,
|
|
11
|
+
add_segmentation_processing_options,
|
|
12
|
+
add_workers_option,
|
|
13
|
+
)
|
|
14
|
+
from copick_utils.util.config_models import create_simple_config
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.command(
|
|
18
|
+
context_settings={"show_default": True},
|
|
19
|
+
short_help="Convert segmentation to picks.",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
@add_config_option
|
|
23
|
+
@optgroup.group("\nInput Options", help="Options related to the input segmentations.")
|
|
24
|
+
@optgroup.option(
|
|
25
|
+
"--run-names",
|
|
26
|
+
"-r",
|
|
27
|
+
multiple=True,
|
|
28
|
+
help="Specific run names to process (default: all runs).",
|
|
29
|
+
)
|
|
30
|
+
@add_input_option("segmentation")
|
|
31
|
+
@optgroup.group("\nTool Options", help="Options related to this tool.")
|
|
32
|
+
@add_segmentation_processing_options
|
|
33
|
+
@add_workers_option
|
|
34
|
+
@optgroup.group("\nOutput Options", help="Options related to output picks.")
|
|
35
|
+
@add_output_option("picks", default_tool="seg2picks")
|
|
36
|
+
@add_debug_option
|
|
37
|
+
def seg2picks(
|
|
38
|
+
config,
|
|
39
|
+
run_names,
|
|
40
|
+
input_uri,
|
|
41
|
+
segmentation_idx,
|
|
42
|
+
maxima_filter_size,
|
|
43
|
+
min_particle_size,
|
|
44
|
+
max_particle_size,
|
|
45
|
+
workers,
|
|
46
|
+
output_uri,
|
|
47
|
+
debug,
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
Convert segmentation volumes to picks by extracting centroids.
|
|
51
|
+
|
|
52
|
+
\b
|
|
53
|
+
URI Format:
|
|
54
|
+
Segmentations: name:user_id/session_id@voxel_spacing
|
|
55
|
+
Picks: object_name:user_id/session_id
|
|
56
|
+
|
|
57
|
+
\b
|
|
58
|
+
Examples:
|
|
59
|
+
# Convert single segmentation to picks
|
|
60
|
+
copick convert seg2picks -i "membrane:user1/manual-001@10.0" -o "membrane:seg2picks/centroid-001"
|
|
61
|
+
|
|
62
|
+
# Convert all manual segmentations using pattern matching
|
|
63
|
+
copick convert seg2picks -i "membrane:user1/manual-.*@10.0" -o "membrane:seg2picks/centroid-{input_session_id}"
|
|
64
|
+
"""
|
|
65
|
+
from copick_utils.converters.picks_from_segmentation import picks_from_segmentation_lazy_batch
|
|
66
|
+
|
|
67
|
+
logger = get_logger(__name__, debug=debug)
|
|
68
|
+
|
|
69
|
+
root = copick.from_file(config)
|
|
70
|
+
run_names_list = list(run_names) if run_names else None
|
|
71
|
+
|
|
72
|
+
# Create config directly from URIs with smart defaults
|
|
73
|
+
try:
|
|
74
|
+
task_config = create_simple_config(
|
|
75
|
+
input_uri=input_uri,
|
|
76
|
+
input_type="segmentation",
|
|
77
|
+
output_uri=output_uri,
|
|
78
|
+
output_type="picks",
|
|
79
|
+
command_name="seg2picks",
|
|
80
|
+
)
|
|
81
|
+
except ValueError as e:
|
|
82
|
+
raise click.BadParameter(str(e)) from e
|
|
83
|
+
|
|
84
|
+
# Extract parameters for logging
|
|
85
|
+
input_params = parse_copick_uri(input_uri, "segmentation")
|
|
86
|
+
output_params = parse_copick_uri(output_uri, "picks")
|
|
87
|
+
|
|
88
|
+
logger.info(f"Converting segmentation to picks for '{input_params['name']}'")
|
|
89
|
+
logger.info(
|
|
90
|
+
f"Source segmentation pattern: {input_params['name']} ({input_params['user_id']}/{input_params['session_id']})",
|
|
91
|
+
)
|
|
92
|
+
logger.info(
|
|
93
|
+
f"Target picks template: {output_params['object_name']} ({output_params['user_id']}/{output_params['session_id']})",
|
|
94
|
+
)
|
|
95
|
+
logger.info(f"Label {segmentation_idx}, particle size: {min_particle_size}-{max_particle_size}")
|
|
96
|
+
|
|
97
|
+
# Parallel discovery and processing - no sequential bottleneck!
|
|
98
|
+
results = picks_from_segmentation_lazy_batch(
|
|
99
|
+
root=root,
|
|
100
|
+
config=task_config,
|
|
101
|
+
run_names=run_names_list,
|
|
102
|
+
workers=workers,
|
|
103
|
+
segmentation_idx=segmentation_idx,
|
|
104
|
+
maxima_filter_size=maxima_filter_size,
|
|
105
|
+
min_particle_size=min_particle_size,
|
|
106
|
+
max_particle_size=max_particle_size,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
|
|
110
|
+
total_points = sum(result.get("points_created", 0) for result in results.values() if result)
|
|
111
|
+
total_processed = sum(result.get("processed", 0) for result in results.values() if result)
|
|
112
|
+
|
|
113
|
+
# Collect all errors
|
|
114
|
+
all_errors = []
|
|
115
|
+
for result in results.values():
|
|
116
|
+
if result and result.get("errors"):
|
|
117
|
+
all_errors.extend(result["errors"])
|
|
118
|
+
|
|
119
|
+
logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
|
|
120
|
+
logger.info(f"Total conversion tasks completed: {total_processed}")
|
|
121
|
+
logger.info(f"Total points created: {total_points}")
|
|
122
|
+
|
|
123
|
+
if all_errors:
|
|
124
|
+
logger.warning(f"Encountered {len(all_errors)} errors during processing")
|
|
125
|
+
for error in all_errors[:5]: # Show first 5 errors
|
|
126
|
+
logger.warning(f" - {error}")
|
|
127
|
+
if len(all_errors) > 5:
|
|
128
|
+
logger.warning(f" ... and {len(all_errors) - 5} more errors")
|