copick-utils 0.6.1__py3-none-any.whl → 1.0.1__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 -1
- 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 +123 -13
- copick_utils/converters/sphere_from_picks.py +306 -0
- copick_utils/converters/surface_from_picks.py +337 -0
- 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/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.1.dist-info → copick_utils-1.0.1.dist-info}/METADATA +15 -2
- copick_utils-1.0.1.dist-info/RECORD +71 -0
- {copick_utils-0.6.1.dist-info → copick_utils-1.0.1.dist-info}/WHEEL +1 -1
- copick_utils-1.0.1.dist-info/entry_points.txt +29 -0
- copick_utils/segmentation/picks_from_segmentation.py +0 -81
- copick_utils-0.6.1.dist-info/RECORD +0 -14
- /copick_utils/{segmentation → io}/__init__.py +0 -0
- {copick_utils-0.6.1.dist-info → copick_utils-1.0.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
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_clustering_options,
|
|
10
|
+
add_input_option,
|
|
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 picks to plane meshes.",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
@add_config_option
|
|
23
|
+
@optgroup.group("\nInput Options", help="Options related to the input picks.")
|
|
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("picks")
|
|
31
|
+
@optgroup.group("\nTool Options", help="Options related to this tool.")
|
|
32
|
+
@optgroup.option(
|
|
33
|
+
"--padding",
|
|
34
|
+
type=float,
|
|
35
|
+
default=1.2,
|
|
36
|
+
help="Padding factor for plane size (1.0=exact fit, >1.0=larger plane).",
|
|
37
|
+
)
|
|
38
|
+
@add_clustering_options
|
|
39
|
+
@add_workers_option
|
|
40
|
+
@optgroup.group("\nOutput Options", help="Options related to output meshes.")
|
|
41
|
+
@add_output_option("mesh", default_tool="picks2plane")
|
|
42
|
+
@optgroup.option(
|
|
43
|
+
"--individual-meshes/--no-individual-meshes",
|
|
44
|
+
"-im",
|
|
45
|
+
is_flag=True,
|
|
46
|
+
default=False,
|
|
47
|
+
help="Create individual meshes for each instance (enables {instance_id} placeholder).",
|
|
48
|
+
)
|
|
49
|
+
@add_debug_option
|
|
50
|
+
def picks2plane(
|
|
51
|
+
config,
|
|
52
|
+
run_names,
|
|
53
|
+
input_uri,
|
|
54
|
+
use_clustering,
|
|
55
|
+
clustering_method,
|
|
56
|
+
clustering_eps,
|
|
57
|
+
clustering_min_samples,
|
|
58
|
+
clustering_n_clusters,
|
|
59
|
+
padding,
|
|
60
|
+
all_clusters,
|
|
61
|
+
workers,
|
|
62
|
+
output_uri,
|
|
63
|
+
individual_meshes,
|
|
64
|
+
debug,
|
|
65
|
+
):
|
|
66
|
+
"""
|
|
67
|
+
Convert picks to plane meshes.
|
|
68
|
+
|
|
69
|
+
\b
|
|
70
|
+
URI Format:
|
|
71
|
+
Picks: object_name:user_id/session_id
|
|
72
|
+
Meshes: object_name:user_id/session_id
|
|
73
|
+
|
|
74
|
+
\b
|
|
75
|
+
Examples:
|
|
76
|
+
# Convert single pick set to single plane mesh
|
|
77
|
+
copick convert picks2plane -i "membrane:user1/manual-001" -o "membrane:picks2plane/plane-001"
|
|
78
|
+
|
|
79
|
+
# Create individual plane meshes from clusters
|
|
80
|
+
copick convert picks2plane -i "membrane:user1/manual-001" -o "membrane:picks2plane/plane-{instance_id}" --individual-meshes
|
|
81
|
+
|
|
82
|
+
# Convert all manual picks using pattern matching
|
|
83
|
+
copick convert picks2plane -i "membrane:user1/manual-.*" -o "membrane:picks2plane/plane-{input_session_id}"
|
|
84
|
+
"""
|
|
85
|
+
from copick_utils.converters.plane_from_picks import plane_from_picks_lazy_batch
|
|
86
|
+
|
|
87
|
+
logger = get_logger(__name__, debug=debug)
|
|
88
|
+
|
|
89
|
+
root = copick.from_file(config)
|
|
90
|
+
run_names_list = list(run_names) if run_names else None
|
|
91
|
+
|
|
92
|
+
# Create config directly from URIs with smart defaults
|
|
93
|
+
try:
|
|
94
|
+
task_config = create_simple_config(
|
|
95
|
+
input_uri=input_uri,
|
|
96
|
+
input_type="picks",
|
|
97
|
+
output_uri=output_uri,
|
|
98
|
+
output_type="mesh",
|
|
99
|
+
individual_outputs=individual_meshes,
|
|
100
|
+
command_name="picks2plane",
|
|
101
|
+
)
|
|
102
|
+
except ValueError as e:
|
|
103
|
+
raise click.BadParameter(str(e)) from e
|
|
104
|
+
|
|
105
|
+
# Extract parameters for logging
|
|
106
|
+
input_params = parse_copick_uri(input_uri, "picks")
|
|
107
|
+
output_params = parse_copick_uri(output_uri, "mesh")
|
|
108
|
+
|
|
109
|
+
# Prepare clustering parameters
|
|
110
|
+
clustering_params = {}
|
|
111
|
+
if clustering_method == "dbscan":
|
|
112
|
+
clustering_params = {"eps": clustering_eps, "min_samples": clustering_min_samples}
|
|
113
|
+
elif clustering_method == "kmeans":
|
|
114
|
+
clustering_params = {"n_clusters": clustering_n_clusters}
|
|
115
|
+
|
|
116
|
+
logger.info(f"Converting picks to plane mesh for object '{input_params['object_name']}'")
|
|
117
|
+
logger.info(f"Source picks pattern: {input_params['user_id']}/{input_params['session_id']}")
|
|
118
|
+
logger.info(
|
|
119
|
+
f"Target mesh template: {output_params['object_name']} ({output_params['user_id']}/{output_params['session_id']})",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Parallel discovery and processing - no sequential bottleneck!
|
|
123
|
+
results = plane_from_picks_lazy_batch(
|
|
124
|
+
root=root,
|
|
125
|
+
config=task_config,
|
|
126
|
+
run_names=run_names_list,
|
|
127
|
+
workers=workers,
|
|
128
|
+
use_clustering=use_clustering,
|
|
129
|
+
clustering_method=clustering_method,
|
|
130
|
+
clustering_params=clustering_params,
|
|
131
|
+
padding=padding,
|
|
132
|
+
all_clusters=all_clusters,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
|
|
136
|
+
total_vertices = sum(result.get("vertices_created", 0) for result in results.values() if result)
|
|
137
|
+
total_faces = sum(result.get("faces_created", 0) for result in results.values() if result)
|
|
138
|
+
total_processed = sum(result.get("processed", 0) for result in results.values() if result)
|
|
139
|
+
|
|
140
|
+
# Collect all errors
|
|
141
|
+
all_errors = []
|
|
142
|
+
for result in results.values():
|
|
143
|
+
if result and result.get("errors"):
|
|
144
|
+
all_errors.extend(result["errors"])
|
|
145
|
+
|
|
146
|
+
logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
|
|
147
|
+
logger.info(f"Total conversion tasks completed: {total_processed}")
|
|
148
|
+
logger.info(f"Total vertices created: {total_vertices}")
|
|
149
|
+
logger.info(f"Total faces created: {total_faces}")
|
|
150
|
+
|
|
151
|
+
if all_errors:
|
|
152
|
+
logger.warning(f"Encountered {len(all_errors)} errors during processing")
|
|
153
|
+
for error in all_errors[:5]: # Show first 5 errors
|
|
154
|
+
logger.warning(f" - {error}")
|
|
155
|
+
if len(all_errors) > 5:
|
|
156
|
+
logger.warning(f" ... and {len(all_errors) - 5} more errors")
|
|
@@ -0,0 +1,134 @@
|
|
|
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_picks_painting_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 picks to segmentation.",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
@add_config_option
|
|
23
|
+
@optgroup.group("\nInput Options", help="Options related to the input picks.")
|
|
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("picks")
|
|
31
|
+
@optgroup.group("\nTool Options", help="Options related to this tool.")
|
|
32
|
+
@add_picks_painting_options
|
|
33
|
+
@optgroup.option(
|
|
34
|
+
"--tomo-type",
|
|
35
|
+
"-tt",
|
|
36
|
+
default="wbp",
|
|
37
|
+
help="Type of tomogram to use as reference.",
|
|
38
|
+
)
|
|
39
|
+
@add_workers_option
|
|
40
|
+
@optgroup.group("\nOutput Options", help="Options related to output segmentations.")
|
|
41
|
+
@add_output_option("segmentation", default_tool="picks2seg")
|
|
42
|
+
@add_debug_option
|
|
43
|
+
def picks2seg(
|
|
44
|
+
config,
|
|
45
|
+
run_names,
|
|
46
|
+
input_uri,
|
|
47
|
+
radius,
|
|
48
|
+
tomo_type,
|
|
49
|
+
workers,
|
|
50
|
+
output_uri,
|
|
51
|
+
debug,
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
Convert picks to segmentation volumes by painting spheres.
|
|
55
|
+
|
|
56
|
+
\b
|
|
57
|
+
URI Format:
|
|
58
|
+
Picks: object_name:user_id/session_id
|
|
59
|
+
Segmentations: name:user_id/session_id@voxel_spacing
|
|
60
|
+
|
|
61
|
+
\b
|
|
62
|
+
Examples:
|
|
63
|
+
# Convert single pick set to segmentation
|
|
64
|
+
copick convert picks2seg -i "ribosome:user1/manual-001" -o "ribosome:picks2seg/painted-001@10.0"
|
|
65
|
+
|
|
66
|
+
# Convert all manual picks using pattern matching
|
|
67
|
+
copick convert picks2seg -i "ribosome:user1/manual-.*" -o "ribosome:picks2seg/painted-{input_session_id}@10.0"
|
|
68
|
+
"""
|
|
69
|
+
from copick_utils.converters.segmentation_from_picks import segmentation_from_picks_lazy_batch
|
|
70
|
+
|
|
71
|
+
logger = get_logger(__name__, debug=debug)
|
|
72
|
+
|
|
73
|
+
root = copick.from_file(config)
|
|
74
|
+
run_names_list = list(run_names) if run_names else None
|
|
75
|
+
|
|
76
|
+
# Create config directly from URIs with smart defaults
|
|
77
|
+
try:
|
|
78
|
+
task_config = create_simple_config(
|
|
79
|
+
input_uri=input_uri,
|
|
80
|
+
input_type="picks",
|
|
81
|
+
output_uri=output_uri,
|
|
82
|
+
output_type="segmentation",
|
|
83
|
+
command_name="picks2seg",
|
|
84
|
+
)
|
|
85
|
+
except ValueError as e:
|
|
86
|
+
raise click.BadParameter(str(e)) from e
|
|
87
|
+
|
|
88
|
+
# Extract parameters for logging
|
|
89
|
+
input_params = parse_copick_uri(input_uri, "picks")
|
|
90
|
+
output_params = parse_copick_uri(output_uri, "segmentation")
|
|
91
|
+
|
|
92
|
+
voxel_spacing = output_params["voxel_spacing"]
|
|
93
|
+
if isinstance(voxel_spacing, str):
|
|
94
|
+
voxel_spacing = float(voxel_spacing)
|
|
95
|
+
|
|
96
|
+
logger.info(f"Converting picks to segmentation for object '{input_params['object_name']}'")
|
|
97
|
+
logger.info(f"Source picks pattern: {input_params['user_id']}/{input_params['session_id']}")
|
|
98
|
+
logger.info(
|
|
99
|
+
f"Target segmentation template: {output_params['name']} ({output_params['user_id']}/{output_params['session_id']})",
|
|
100
|
+
)
|
|
101
|
+
logger.info(f"Sphere radius: {radius}, voxel spacing: {voxel_spacing}")
|
|
102
|
+
|
|
103
|
+
# Parallel discovery and processing - no sequential bottleneck!
|
|
104
|
+
results = segmentation_from_picks_lazy_batch(
|
|
105
|
+
root=root,
|
|
106
|
+
config=task_config,
|
|
107
|
+
run_names=run_names_list,
|
|
108
|
+
workers=workers,
|
|
109
|
+
radius=radius,
|
|
110
|
+
tomo_type=tomo_type,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
|
|
114
|
+
total_points = sum(result.get("points_converted", 0) for result in results.values() if result)
|
|
115
|
+
total_voxels = sum(result.get("voxels_created", 0) for result in results.values() if result)
|
|
116
|
+
total_processed = sum(result.get("processed", 0) for result in results.values() if result)
|
|
117
|
+
|
|
118
|
+
# Collect all errors
|
|
119
|
+
all_errors = []
|
|
120
|
+
for result in results.values():
|
|
121
|
+
if result and result.get("errors"):
|
|
122
|
+
all_errors.extend(result["errors"])
|
|
123
|
+
|
|
124
|
+
logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
|
|
125
|
+
logger.info(f"Total conversion tasks completed: {total_processed}")
|
|
126
|
+
logger.info(f"Total points converted: {total_points}")
|
|
127
|
+
logger.info(f"Total voxels created: {total_voxels}")
|
|
128
|
+
|
|
129
|
+
if all_errors:
|
|
130
|
+
logger.warning(f"Encountered {len(all_errors)} errors during processing")
|
|
131
|
+
for error in all_errors[:5]: # Show first 5 errors
|
|
132
|
+
logger.warning(f" - {error}")
|
|
133
|
+
if len(all_errors) > 5:
|
|
134
|
+
logger.warning(f" ... and {len(all_errors) - 5} more errors")
|
|
@@ -0,0 +1,170 @@
|
|
|
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_clustering_options,
|
|
10
|
+
add_input_option,
|
|
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 picks to sphere meshes.",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
@add_config_option
|
|
23
|
+
@optgroup.group("\nInput Options", help="Options related to the input picks.")
|
|
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("picks")
|
|
31
|
+
@optgroup.group("\nTool Options", help="Options related to this tool.")
|
|
32
|
+
@optgroup.option(
|
|
33
|
+
"--subdivisions",
|
|
34
|
+
type=int,
|
|
35
|
+
default=2,
|
|
36
|
+
help="Number of sphere subdivisions for mesh resolution.",
|
|
37
|
+
)
|
|
38
|
+
@optgroup.option(
|
|
39
|
+
"--deduplicate-spheres/--no-deduplicate-spheres",
|
|
40
|
+
is_flag=True,
|
|
41
|
+
default=True,
|
|
42
|
+
help="Merge overlapping spheres to avoid duplicates.",
|
|
43
|
+
)
|
|
44
|
+
@optgroup.option(
|
|
45
|
+
"--min-sphere-distance",
|
|
46
|
+
type=float,
|
|
47
|
+
help="Minimum distance between sphere centers for deduplication (default: 0.5 * average radius).",
|
|
48
|
+
)
|
|
49
|
+
@add_clustering_options
|
|
50
|
+
@add_workers_option
|
|
51
|
+
@optgroup.group("\nOutput Options", help="Options related to output meshes.")
|
|
52
|
+
@add_output_option("mesh", default_tool="picks2sphere")
|
|
53
|
+
@optgroup.option(
|
|
54
|
+
"--individual-meshes/--no-individual-meshes",
|
|
55
|
+
"-im",
|
|
56
|
+
is_flag=True,
|
|
57
|
+
default=False,
|
|
58
|
+
help="Create individual meshes for each instance (enables {instance_id} placeholder).",
|
|
59
|
+
)
|
|
60
|
+
@add_debug_option
|
|
61
|
+
def picks2sphere(
|
|
62
|
+
config,
|
|
63
|
+
run_names,
|
|
64
|
+
input_uri,
|
|
65
|
+
use_clustering,
|
|
66
|
+
clustering_method,
|
|
67
|
+
clustering_eps,
|
|
68
|
+
clustering_min_samples,
|
|
69
|
+
clustering_n_clusters,
|
|
70
|
+
subdivisions,
|
|
71
|
+
deduplicate_spheres,
|
|
72
|
+
min_sphere_distance,
|
|
73
|
+
all_clusters,
|
|
74
|
+
workers,
|
|
75
|
+
output_uri,
|
|
76
|
+
individual_meshes,
|
|
77
|
+
debug,
|
|
78
|
+
):
|
|
79
|
+
"""
|
|
80
|
+
Convert picks to sphere meshes.
|
|
81
|
+
|
|
82
|
+
\b
|
|
83
|
+
URI Format:
|
|
84
|
+
Picks: object_name:user_id/session_id
|
|
85
|
+
Meshes: object_name:user_id/session_id
|
|
86
|
+
|
|
87
|
+
\b
|
|
88
|
+
Examples:
|
|
89
|
+
# Convert single pick set to single sphere mesh
|
|
90
|
+
copick convert picks2sphere -i "ribosome:user1/manual-001" -o "ribosome:picks2sphere/sphere-001"
|
|
91
|
+
|
|
92
|
+
# Create individual sphere meshes
|
|
93
|
+
copick convert picks2sphere -i "ribosome:user1/manual-001" -o "ribosome:picks2sphere/sphere-{instance_id}" --individual-meshes
|
|
94
|
+
|
|
95
|
+
# Convert all manual picks using pattern matching
|
|
96
|
+
copick convert picks2sphere -i "ribosome:user1/manual-.*" -o "ribosome:picks2sphere/sphere-{input_session_id}"
|
|
97
|
+
"""
|
|
98
|
+
from copick_utils.converters.sphere_from_picks import sphere_from_picks_lazy_batch
|
|
99
|
+
|
|
100
|
+
logger = get_logger(__name__, debug=debug)
|
|
101
|
+
|
|
102
|
+
root = copick.from_file(config)
|
|
103
|
+
run_names_list = list(run_names) if run_names else None
|
|
104
|
+
|
|
105
|
+
# Create config directly from URIs with smart defaults
|
|
106
|
+
try:
|
|
107
|
+
task_config = create_simple_config(
|
|
108
|
+
input_uri=input_uri,
|
|
109
|
+
input_type="picks",
|
|
110
|
+
output_uri=output_uri,
|
|
111
|
+
output_type="mesh",
|
|
112
|
+
individual_outputs=individual_meshes,
|
|
113
|
+
command_name="picks2sphere",
|
|
114
|
+
)
|
|
115
|
+
except ValueError as e:
|
|
116
|
+
raise click.BadParameter(str(e)) from e
|
|
117
|
+
|
|
118
|
+
# Extract parameters for logging
|
|
119
|
+
input_params = parse_copick_uri(input_uri, "picks")
|
|
120
|
+
output_params = parse_copick_uri(output_uri, "mesh")
|
|
121
|
+
|
|
122
|
+
# Prepare clustering parameters
|
|
123
|
+
clustering_params = {}
|
|
124
|
+
if clustering_method == "dbscan":
|
|
125
|
+
clustering_params = {"eps": clustering_eps, "min_samples": clustering_min_samples}
|
|
126
|
+
elif clustering_method == "kmeans":
|
|
127
|
+
clustering_params = {"n_clusters": clustering_n_clusters}
|
|
128
|
+
|
|
129
|
+
logger.info(f"Converting picks to sphere mesh for object '{input_params['object_name']}'")
|
|
130
|
+
logger.info(f"Source picks pattern: {input_params['user_id']}/{input_params['session_id']}")
|
|
131
|
+
logger.info(
|
|
132
|
+
f"Target mesh template: {output_params['object_name']} ({output_params['user_id']}/{output_params['session_id']})",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Parallel discovery and processing - no sequential bottleneck!
|
|
136
|
+
results = sphere_from_picks_lazy_batch(
|
|
137
|
+
root=root,
|
|
138
|
+
config=task_config,
|
|
139
|
+
run_names=run_names_list,
|
|
140
|
+
workers=workers,
|
|
141
|
+
use_clustering=use_clustering,
|
|
142
|
+
clustering_method=clustering_method,
|
|
143
|
+
clustering_params=clustering_params,
|
|
144
|
+
subdivisions=subdivisions,
|
|
145
|
+
all_clusters=all_clusters,
|
|
146
|
+
deduplicate_spheres_flag=deduplicate_spheres,
|
|
147
|
+
min_sphere_distance=min_sphere_distance,
|
|
148
|
+
)
|
|
149
|
+
successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
|
|
150
|
+
total_vertices = sum(result.get("vertices_created", 0) for result in results.values() if result)
|
|
151
|
+
total_faces = sum(result.get("faces_created", 0) for result in results.values() if result)
|
|
152
|
+
total_processed = sum(result.get("processed", 0) for result in results.values() if result)
|
|
153
|
+
|
|
154
|
+
# Collect all errors
|
|
155
|
+
all_errors = []
|
|
156
|
+
for result in results.values():
|
|
157
|
+
if result and result.get("errors"):
|
|
158
|
+
all_errors.extend(result["errors"])
|
|
159
|
+
|
|
160
|
+
logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
|
|
161
|
+
logger.info(f"Total conversion tasks completed: {total_processed}")
|
|
162
|
+
logger.info(f"Total vertices created: {total_vertices}")
|
|
163
|
+
logger.info(f"Total faces created: {total_faces}")
|
|
164
|
+
|
|
165
|
+
if all_errors:
|
|
166
|
+
logger.warning(f"Encountered {len(all_errors)} errors during processing")
|
|
167
|
+
for error in all_errors[:5]: # Show first 5 errors
|
|
168
|
+
logger.warning(f" - {error}")
|
|
169
|
+
if len(all_errors) > 5:
|
|
170
|
+
logger.warning(f" ... and {len(all_errors) - 5} more errors")
|
|
@@ -0,0 +1,164 @@
|
|
|
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_clustering_options,
|
|
10
|
+
add_input_option,
|
|
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 picks to 2D surface meshes.",
|
|
20
|
+
no_args_is_help=True,
|
|
21
|
+
)
|
|
22
|
+
@add_config_option
|
|
23
|
+
@optgroup.group("\nInput Options", help="Options related to the input picks.")
|
|
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("picks")
|
|
31
|
+
@optgroup.group("\nTool Options", help="Options related to this tool.")
|
|
32
|
+
@optgroup.option(
|
|
33
|
+
"--surface-method",
|
|
34
|
+
type=click.Choice(["delaunay", "rbf", "grid"]),
|
|
35
|
+
default="delaunay",
|
|
36
|
+
help="Surface fitting method.",
|
|
37
|
+
)
|
|
38
|
+
@optgroup.option(
|
|
39
|
+
"--grid-resolution",
|
|
40
|
+
type=int,
|
|
41
|
+
default=50,
|
|
42
|
+
help="Resolution for grid-based surface methods.",
|
|
43
|
+
)
|
|
44
|
+
@add_clustering_options
|
|
45
|
+
@add_workers_option
|
|
46
|
+
@optgroup.group("\nOutput Options", help="Options related to output meshes.")
|
|
47
|
+
@add_output_option("mesh", default_tool="picks2surface")
|
|
48
|
+
@optgroup.option(
|
|
49
|
+
"--individual-meshes/--no-individual-meshes",
|
|
50
|
+
"-im",
|
|
51
|
+
is_flag=True,
|
|
52
|
+
default=False,
|
|
53
|
+
help="Create individual meshes for each instance (enables {instance_id} placeholder).",
|
|
54
|
+
)
|
|
55
|
+
@add_debug_option
|
|
56
|
+
def picks2surface(
|
|
57
|
+
config,
|
|
58
|
+
run_names,
|
|
59
|
+
input_uri,
|
|
60
|
+
surface_method,
|
|
61
|
+
grid_resolution,
|
|
62
|
+
use_clustering,
|
|
63
|
+
clustering_method,
|
|
64
|
+
clustering_eps,
|
|
65
|
+
clustering_min_samples,
|
|
66
|
+
clustering_n_clusters,
|
|
67
|
+
all_clusters,
|
|
68
|
+
workers,
|
|
69
|
+
output_uri,
|
|
70
|
+
individual_meshes,
|
|
71
|
+
debug,
|
|
72
|
+
):
|
|
73
|
+
"""
|
|
74
|
+
Convert picks to 2D surface meshes.
|
|
75
|
+
|
|
76
|
+
\b
|
|
77
|
+
URI Format:
|
|
78
|
+
Picks: object_name:user_id/session_id
|
|
79
|
+
Meshes: object_name:user_id/session_id
|
|
80
|
+
|
|
81
|
+
\b
|
|
82
|
+
Examples:
|
|
83
|
+
# Convert single pick set to single surface mesh
|
|
84
|
+
copick convert picks2surface -i "membrane:user1/manual-001" -o "membrane:picks2surface/surface-001"
|
|
85
|
+
|
|
86
|
+
# Create individual surface meshes from clusters
|
|
87
|
+
copick convert picks2surface -i "membrane:user1/manual-001" -o "membrane:picks2surface/surface-{instance_id}" --individual-meshes
|
|
88
|
+
|
|
89
|
+
# Convert all manual picks using pattern matching
|
|
90
|
+
copick convert picks2surface -i "membrane:user1/manual-.*" -o "membrane:picks2surface/surface-{input_session_id}"
|
|
91
|
+
"""
|
|
92
|
+
from copick_utils.converters.surface_from_picks import surface_from_picks_lazy_batch
|
|
93
|
+
|
|
94
|
+
logger = get_logger(__name__, debug=debug)
|
|
95
|
+
|
|
96
|
+
root = copick.from_file(config)
|
|
97
|
+
run_names_list = list(run_names) if run_names else None
|
|
98
|
+
|
|
99
|
+
# Create config directly from URIs with smart defaults
|
|
100
|
+
try:
|
|
101
|
+
task_config = create_simple_config(
|
|
102
|
+
input_uri=input_uri,
|
|
103
|
+
input_type="picks",
|
|
104
|
+
output_uri=output_uri,
|
|
105
|
+
output_type="mesh",
|
|
106
|
+
individual_outputs=individual_meshes,
|
|
107
|
+
command_name="picks2surface",
|
|
108
|
+
)
|
|
109
|
+
except ValueError as e:
|
|
110
|
+
raise click.BadParameter(str(e)) from e
|
|
111
|
+
|
|
112
|
+
# Extract parameters for logging
|
|
113
|
+
input_params = parse_copick_uri(input_uri, "picks")
|
|
114
|
+
output_params = parse_copick_uri(output_uri, "mesh")
|
|
115
|
+
|
|
116
|
+
# Prepare clustering parameters
|
|
117
|
+
clustering_params = {}
|
|
118
|
+
if clustering_method == "dbscan":
|
|
119
|
+
clustering_params = {"eps": clustering_eps, "min_samples": clustering_min_samples}
|
|
120
|
+
elif clustering_method == "kmeans":
|
|
121
|
+
clustering_params = {"n_clusters": clustering_n_clusters}
|
|
122
|
+
|
|
123
|
+
logger.info(f"Converting picks to {surface_method} surface mesh for object '{input_params['object_name']}'")
|
|
124
|
+
logger.info(f"Source picks pattern: {input_params['user_id']}/{input_params['session_id']}")
|
|
125
|
+
logger.info(
|
|
126
|
+
f"Target mesh template: {output_params['object_name']} ({output_params['user_id']}/{output_params['session_id']})",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# Parallel discovery and processing - no sequential bottleneck!
|
|
130
|
+
results = surface_from_picks_lazy_batch(
|
|
131
|
+
root=root,
|
|
132
|
+
config=task_config,
|
|
133
|
+
run_names=run_names_list,
|
|
134
|
+
workers=workers,
|
|
135
|
+
surface_method=surface_method,
|
|
136
|
+
grid_resolution=grid_resolution,
|
|
137
|
+
use_clustering=use_clustering,
|
|
138
|
+
clustering_method=clustering_method,
|
|
139
|
+
clustering_params=clustering_params,
|
|
140
|
+
all_clusters=all_clusters,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
|
|
144
|
+
total_vertices = sum(result.get("vertices_created", 0) for result in results.values() if result)
|
|
145
|
+
total_faces = sum(result.get("faces_created", 0) for result in results.values() if result)
|
|
146
|
+
total_processed = sum(result.get("processed", 0) for result in results.values() if result)
|
|
147
|
+
|
|
148
|
+
# Collect all errors
|
|
149
|
+
all_errors = []
|
|
150
|
+
for result in results.values():
|
|
151
|
+
if result and result.get("errors"):
|
|
152
|
+
all_errors.extend(result["errors"])
|
|
153
|
+
|
|
154
|
+
logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
|
|
155
|
+
logger.info(f"Total conversion tasks completed: {total_processed}")
|
|
156
|
+
logger.info(f"Total vertices created: {total_vertices}")
|
|
157
|
+
logger.info(f"Total faces created: {total_faces}")
|
|
158
|
+
|
|
159
|
+
if all_errors:
|
|
160
|
+
logger.warning(f"Encountered {len(all_errors)} errors during processing")
|
|
161
|
+
for error in all_errors[:5]: # Show first 5 errors
|
|
162
|
+
logger.warning(f" - {error}")
|
|
163
|
+
if len(all_errors) > 5:
|
|
164
|
+
logger.warning(f" ... and {len(all_errors) - 5} more errors")
|