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.
Files changed (72) hide show
  1. copick_utils/__init__.py +1 -0
  2. copick_utils/cli/__init__.py +33 -0
  3. copick_utils/cli/clipmesh.py +161 -0
  4. copick_utils/cli/clippicks.py +154 -0
  5. copick_utils/cli/clipseg.py +163 -0
  6. copick_utils/cli/conversion_commands.py +32 -0
  7. copick_utils/cli/enclosed.py +191 -0
  8. copick_utils/cli/filter_components.py +166 -0
  9. copick_utils/cli/fit_spline.py +191 -0
  10. copick_utils/cli/hull.py +138 -0
  11. copick_utils/cli/input_output_selection.py +76 -0
  12. copick_utils/cli/logical_commands.py +29 -0
  13. copick_utils/cli/mesh2picks.py +170 -0
  14. copick_utils/cli/mesh2seg.py +167 -0
  15. copick_utils/cli/meshop.py +262 -0
  16. copick_utils/cli/picks2ellipsoid.py +171 -0
  17. copick_utils/cli/picks2mesh.py +181 -0
  18. copick_utils/cli/picks2plane.py +156 -0
  19. copick_utils/cli/picks2seg.py +134 -0
  20. copick_utils/cli/picks2sphere.py +170 -0
  21. copick_utils/cli/picks2surface.py +164 -0
  22. copick_utils/cli/picksin.py +146 -0
  23. copick_utils/cli/picksout.py +148 -0
  24. copick_utils/cli/processing_commands.py +18 -0
  25. copick_utils/cli/seg2mesh.py +135 -0
  26. copick_utils/cli/seg2picks.py +128 -0
  27. copick_utils/cli/segop.py +248 -0
  28. copick_utils/cli/separate_components.py +155 -0
  29. copick_utils/cli/skeletonize.py +164 -0
  30. copick_utils/cli/util.py +580 -0
  31. copick_utils/cli/validbox.py +155 -0
  32. copick_utils/converters/__init__.py +35 -0
  33. copick_utils/converters/converter_common.py +543 -0
  34. copick_utils/converters/ellipsoid_from_picks.py +335 -0
  35. copick_utils/converters/lazy_converter.py +576 -0
  36. copick_utils/converters/mesh_from_picks.py +209 -0
  37. copick_utils/converters/mesh_from_segmentation.py +119 -0
  38. copick_utils/converters/picks_from_mesh.py +542 -0
  39. copick_utils/converters/picks_from_segmentation.py +168 -0
  40. copick_utils/converters/plane_from_picks.py +251 -0
  41. copick_utils/converters/segmentation_from_mesh.py +291 -0
  42. copick_utils/{segmentation → converters}/segmentation_from_picks.py +151 -15
  43. copick_utils/converters/sphere_from_picks.py +306 -0
  44. copick_utils/converters/surface_from_picks.py +337 -0
  45. copick_utils/features/skimage.py +33 -13
  46. copick_utils/io/readers.py +62 -59
  47. copick_utils/io/writers.py +9 -14
  48. copick_utils/logical/__init__.py +43 -0
  49. copick_utils/logical/distance_operations.py +604 -0
  50. copick_utils/logical/enclosed_operations.py +222 -0
  51. copick_utils/logical/mesh_operations.py +443 -0
  52. copick_utils/logical/point_operations.py +303 -0
  53. copick_utils/logical/segmentation_operations.py +399 -0
  54. copick_utils/pickers/grid_picker.py +5 -4
  55. copick_utils/process/__init__.py +47 -0
  56. copick_utils/process/connected_components.py +360 -0
  57. copick_utils/process/filter_components.py +306 -0
  58. copick_utils/process/hull.py +106 -0
  59. copick_utils/process/skeletonize.py +326 -0
  60. copick_utils/process/spline_fitting.py +648 -0
  61. copick_utils/process/validbox.py +333 -0
  62. copick_utils/util/__init__.py +6 -0
  63. copick_utils/util/config_models.py +614 -0
  64. {copick_utils-0.6.0.dist-info → copick_utils-1.0.0.dist-info}/METADATA +38 -12
  65. copick_utils-1.0.0.dist-info/RECORD +71 -0
  66. {copick_utils-0.6.0.dist-info → copick_utils-1.0.0.dist-info}/WHEEL +1 -1
  67. copick_utils-1.0.0.dist-info/entry_points.txt +29 -0
  68. copick_utils/__about__.py +0 -4
  69. copick_utils/segmentation/picks_from_segmentation.py +0 -67
  70. copick_utils-0.6.0.dist-info/RECORD +0 -15
  71. /copick_utils/{segmentation → io}/__init__.py +0 -0
  72. /copick_utils-0.6.0.dist-info/LICENSE.txt → /copick_utils-1.0.0.dist-info/licenses/LICENSE +0 -0
@@ -0,0 +1,614 @@
1
+ """Pydantic models for lazy task discovery configuration."""
2
+
3
+ from typing import Any, Dict, List, Literal, Optional
4
+
5
+ from pydantic import BaseModel, Field, field_validator
6
+
7
+
8
+ class SelectorConfig(BaseModel):
9
+ """Pydantic model for selector configuration with validation."""
10
+
11
+ input_type: Literal["picks", "mesh", "segmentation"]
12
+ output_type: Literal["picks", "mesh", "segmentation"]
13
+ input_object_name: str
14
+ input_user_id: str
15
+ input_session_id: str
16
+ output_object_name: str
17
+ output_user_id: str = "converter"
18
+ output_session_id: str = "0"
19
+ individual_outputs: bool = False
20
+ segmentation_name: Optional[str] = None
21
+ voxel_spacing: Optional[float] = None
22
+
23
+ @field_validator("segmentation_name")
24
+ @classmethod
25
+ def validate_segmentation_name(cls, v, info):
26
+ """Ensure segmentation_name is provided when needed."""
27
+ values = info.data
28
+ input_type = values.get("input_type")
29
+ output_type = values.get("output_type")
30
+
31
+ if (input_type == "segmentation" or output_type == "segmentation") and v is None:
32
+ raise ValueError("segmentation_name is required when input_type or output_type is 'segmentation'")
33
+ return v
34
+
35
+ @field_validator("voxel_spacing")
36
+ @classmethod
37
+ def validate_voxel_spacing(cls, v, info):
38
+ """Ensure voxel_spacing is provided when working with segmentations."""
39
+ values = info.data
40
+ input_type = values.get("input_type")
41
+ output_type = values.get("output_type")
42
+
43
+ if (input_type == "segmentation" or output_type == "segmentation") and v is None:
44
+ raise ValueError("voxel_spacing is required when working with segmentations")
45
+ return v
46
+
47
+ @field_validator("output_session_id")
48
+ @classmethod
49
+ def validate_output_session_id(cls, v, info):
50
+ """Validate session ID templates contain required placeholders."""
51
+ import re
52
+
53
+ values = info.data
54
+ input_session_id = values.get("input_session_id", "")
55
+ individual_outputs = values.get("individual_outputs", False)
56
+
57
+ # Check if input is a regex pattern
58
+ regex_chars = r"[.*+?^${}()|[\]\\"
59
+ has_regex_chars = any(char in input_session_id for char in regex_chars)
60
+ is_regex = False
61
+ if has_regex_chars:
62
+ try:
63
+ re.compile(input_session_id)
64
+ is_regex = True
65
+ except re.error:
66
+ pass
67
+
68
+ # Validate placeholders
69
+ if individual_outputs and "{instance_id}" not in v:
70
+ raise ValueError("output_session_id must contain {instance_id} placeholder when individual_outputs=True")
71
+
72
+ if is_regex and "{input_session_id}" not in v:
73
+ raise ValueError(
74
+ "output_session_id must contain {input_session_id} placeholder when using regex input pattern",
75
+ )
76
+
77
+ return v
78
+
79
+ @classmethod
80
+ def from_uris(
81
+ cls,
82
+ input_uri: str,
83
+ input_type: Literal["picks", "mesh", "segmentation"],
84
+ output_uri: str,
85
+ output_type: Literal["picks", "mesh", "segmentation"],
86
+ individual_outputs: bool = False,
87
+ command_name: Optional[str] = None,
88
+ ) -> "SelectorConfig":
89
+ """Create SelectorConfig from input/output URIs.
90
+
91
+ Args:
92
+ input_uri: Input copick URI string.
93
+ input_type: Type of input object ('picks', 'mesh', 'segmentation').
94
+ output_uri: Output copick URI string (supports smart defaults).
95
+ output_type: Type of output object ('picks', 'mesh', 'segmentation').
96
+ individual_outputs: Whether to create individual outputs.
97
+ command_name: Name of the command (used for smart defaults in output_uri).
98
+
99
+ Returns:
100
+ SelectorConfig instance with parsed URI components.
101
+
102
+ Raises:
103
+ ValueError: If URI parsing fails or required fields are missing.
104
+ """
105
+ from copick.util.uri import expand_output_uri, parse_copick_uri
106
+
107
+ # Expand output URI with smart defaults
108
+ output_uri = expand_output_uri(
109
+ output_uri=output_uri,
110
+ input_uri=input_uri,
111
+ input_type=input_type,
112
+ output_type=output_type,
113
+ command_name=command_name,
114
+ individual_outputs=individual_outputs,
115
+ )
116
+
117
+ # Parse input URI
118
+ input_params = parse_copick_uri(input_uri, input_type)
119
+
120
+ # Parse output URI (now fully expanded)
121
+ output_params = parse_copick_uri(output_uri, output_type)
122
+
123
+ # Extract common fields
124
+ input_object_name = input_params.get("object_name") or input_params.get("name")
125
+ output_object_name = output_params.get("object_name") or output_params.get("name")
126
+
127
+ # Build config dict
128
+ config_dict = {
129
+ "input_type": input_type,
130
+ "output_type": output_type,
131
+ "input_object_name": input_object_name,
132
+ "input_user_id": input_params["user_id"],
133
+ "input_session_id": input_params["session_id"],
134
+ "output_object_name": output_object_name,
135
+ "output_user_id": output_params["user_id"],
136
+ "output_session_id": output_params["session_id"],
137
+ "individual_outputs": individual_outputs,
138
+ }
139
+
140
+ # Add segmentation-specific fields if needed
141
+ if input_type == "segmentation" or output_type == "segmentation":
142
+ seg_name = input_params.get("name") or output_params.get("name")
143
+ voxel_spacing = input_params.get("voxel_spacing") or output_params.get("voxel_spacing")
144
+
145
+ config_dict["segmentation_name"] = seg_name
146
+
147
+ # Convert voxel_spacing to float if it's a string
148
+ if isinstance(voxel_spacing, str) and voxel_spacing != "*":
149
+ voxel_spacing = float(voxel_spacing)
150
+ config_dict["voxel_spacing"] = voxel_spacing
151
+
152
+ return cls(**config_dict)
153
+
154
+
155
+ class ReferenceConfig(BaseModel):
156
+ """Pydantic model for reference discovery configuration."""
157
+
158
+ reference_type: Literal["mesh", "segmentation"]
159
+ object_name: Optional[str] = None
160
+ user_id: Optional[str] = None
161
+ session_id: Optional[str] = None
162
+ voxel_spacing: Optional[float] = None
163
+ additional_params: Dict[str, Any] = Field(default_factory=dict)
164
+
165
+ @field_validator("voxel_spacing")
166
+ @classmethod
167
+ def validate_segmentation_voxel_spacing(cls, v, info):
168
+ """Ensure voxel_spacing is provided for segmentation references."""
169
+ values = info.data
170
+ if values.get("reference_type") == "segmentation" and v is None:
171
+ raise ValueError("voxel_spacing is required for segmentation references")
172
+ return v
173
+
174
+ @field_validator("object_name")
175
+ @classmethod
176
+ def validate_required_fields(cls, v, info):
177
+ """Ensure required fields are provided."""
178
+ if v is None:
179
+ raise ValueError("object_name is required for reference configuration")
180
+ return v
181
+
182
+ @classmethod
183
+ def from_uri(
184
+ cls,
185
+ uri: str,
186
+ reference_type: Literal["mesh", "segmentation"],
187
+ additional_params: Optional[Dict[str, Any]] = None,
188
+ ) -> "ReferenceConfig":
189
+ """Create ReferenceConfig from a URI.
190
+
191
+ Args:
192
+ uri: Copick URI string for the reference object.
193
+ reference_type: Type of reference ('mesh' or 'segmentation').
194
+ additional_params: Additional parameters to include in the config.
195
+
196
+ Returns:
197
+ ReferenceConfig instance with parsed URI components.
198
+
199
+ Raises:
200
+ ValueError: If URI parsing fails or required fields are missing.
201
+ """
202
+ from copick.util.uri import parse_copick_uri
203
+
204
+ # Parse URI
205
+ params = parse_copick_uri(uri, reference_type)
206
+
207
+ # Extract fields based on type
208
+ object_name = params.get("object_name") or params.get("name")
209
+ user_id = params["user_id"]
210
+ session_id = params["session_id"]
211
+ voxel_spacing = params.get("voxel_spacing")
212
+
213
+ # Convert voxel_spacing to float if it's a string
214
+ if voxel_spacing and isinstance(voxel_spacing, str) and voxel_spacing != "*":
215
+ voxel_spacing = float(voxel_spacing)
216
+
217
+ return cls(
218
+ reference_type=reference_type,
219
+ object_name=object_name,
220
+ user_id=user_id,
221
+ session_id=session_id,
222
+ voxel_spacing=voxel_spacing,
223
+ additional_params=additional_params or {},
224
+ )
225
+
226
+
227
+ class TaskConfig(BaseModel):
228
+ """Pydantic model for complete task configuration."""
229
+
230
+ type: Literal[
231
+ "single_selector",
232
+ "dual_selector",
233
+ "multi_selector",
234
+ "single_selector_with_reference",
235
+ "single_selector_multi_union",
236
+ ]
237
+ selector: Optional[SelectorConfig] = None
238
+ selectors: Optional[List[SelectorConfig]] = None
239
+ reference: Optional[ReferenceConfig] = None
240
+ additional_params: Dict[str, Any] = Field(default_factory=dict)
241
+ pairing_method: Optional[str] = "index_order"
242
+
243
+ @field_validator("selector")
244
+ @classmethod
245
+ def validate_single_selector(cls, v, info):
246
+ """Validate single selector configuration."""
247
+ values = info.data
248
+ config_type = values.get("type")
249
+ if config_type == "single_selector" and v is None:
250
+ raise ValueError("selector is required for single_selector type")
251
+ elif config_type == "single_selector_with_reference" and v is None:
252
+ raise ValueError("selector is required for single_selector_with_reference type")
253
+ elif config_type == "single_selector_multi_union" and v is None:
254
+ raise ValueError("selector is required for single_selector_multi_union type")
255
+ return v
256
+
257
+ @field_validator("selectors")
258
+ @classmethod
259
+ def validate_dual_selectors(cls, v, info):
260
+ """Validate selector configuration."""
261
+ values = info.data
262
+ config_type = values.get("type")
263
+ if config_type == "dual_selector" and (v is None or len(v) != 2):
264
+ raise ValueError("exactly 2 selectors required for dual_selector type")
265
+ if config_type == "multi_selector" and (v is None or len(v) < 2):
266
+ raise ValueError("at least 2 selectors required for multi_selector type")
267
+ return v
268
+
269
+ @field_validator("reference")
270
+ @classmethod
271
+ def validate_reference(cls, v, info):
272
+ """Validate reference configuration."""
273
+ values = info.data
274
+ config_type = values.get("type")
275
+ if config_type == "single_selector_with_reference" and v is None:
276
+ raise ValueError("reference is required for single_selector_with_reference type")
277
+ return v
278
+
279
+
280
+ # URI-based convenience functions (simplified interface)
281
+ def create_simple_config(
282
+ input_uri: str,
283
+ input_type: Literal["picks", "mesh", "segmentation"],
284
+ output_uri: str,
285
+ output_type: Literal["picks", "mesh", "segmentation"],
286
+ individual_outputs: bool = False,
287
+ command_name: Optional[str] = None,
288
+ ) -> TaskConfig:
289
+ """Create a simple single-selector task configuration from URIs.
290
+
291
+ Args:
292
+ input_uri: Input copick URI string.
293
+ input_type: Type of input object.
294
+ output_uri: Output copick URI string (supports smart defaults).
295
+ output_type: Type of output object.
296
+ individual_outputs: Whether to create individual outputs.
297
+ command_name: Name of the command (used for smart defaults in output_uri).
298
+
299
+ Returns:
300
+ TaskConfig instance ready for use.
301
+
302
+ Example:
303
+ config = create_simple_config(
304
+ input_uri="ribosome:user1/manual-001",
305
+ input_type="picks",
306
+ output_uri="ribosome",
307
+ output_type="mesh",
308
+ command_name="picks2mesh",
309
+ )
310
+ """
311
+ selector_config = SelectorConfig.from_uris(
312
+ input_uri=input_uri,
313
+ input_type=input_type,
314
+ output_uri=output_uri,
315
+ output_type=output_type,
316
+ individual_outputs=individual_outputs,
317
+ command_name=command_name,
318
+ )
319
+
320
+ return TaskConfig(type="single_selector", selector=selector_config)
321
+
322
+
323
+ def create_single_selector_config(
324
+ input_uri: str,
325
+ input_type: Literal["mesh", "segmentation"],
326
+ output_uri: str,
327
+ output_type: Literal["mesh", "segmentation"],
328
+ command_name: Optional[str] = None,
329
+ operation: str = "union",
330
+ ) -> TaskConfig:
331
+ """
332
+ Create a single-selector config for pattern expansion to N-way operations.
333
+
334
+ Used when a single input URI with pattern should expand to multiple objects
335
+ within each run, then perform an N-way operation on all matched objects.
336
+
337
+ Args:
338
+ input_uri: Input copick URI string (may contain patterns)
339
+ input_type: Type of input objects
340
+ output_uri: Output copick URI string
341
+ output_type: Type of output object
342
+ command_name: Name of command for smart defaults
343
+ operation: Operation type (currently only "union" supported)
344
+
345
+ Returns:
346
+ TaskConfig with single_selector_multi_union type
347
+
348
+ Raises:
349
+ ValueError: If operation is not "union"
350
+
351
+ Example:
352
+ config = create_single_selector_config(
353
+ input_uri="membrane:user*/manual-*@10.0",
354
+ input_type="segmentation",
355
+ output_uri="merged",
356
+ output_type="segmentation",
357
+ command_name="segop",
358
+ operation="union",
359
+ )
360
+ """
361
+ if operation not in ["union", "concatenate"]:
362
+ raise ValueError(
363
+ f"Single-input pattern expansion only supports 'union' and 'concatenate' operations, got '{operation}'",
364
+ )
365
+
366
+ selector = SelectorConfig.from_uris(
367
+ input_uri=input_uri,
368
+ input_type=input_type,
369
+ output_uri=output_uri,
370
+ output_type=output_type,
371
+ individual_outputs=False,
372
+ command_name=command_name,
373
+ )
374
+
375
+ return TaskConfig(
376
+ type="single_selector_multi_union",
377
+ selector=selector,
378
+ )
379
+
380
+
381
+ def create_dual_selector_config(
382
+ input1_uri: str,
383
+ input2_uri: str,
384
+ input_type: Literal["mesh", "segmentation"],
385
+ output_uri: str,
386
+ output_type: Literal["mesh", "segmentation"],
387
+ pairing_method: str = "index_order",
388
+ individual_outputs: bool = False,
389
+ command_name: Optional[str] = None,
390
+ ) -> TaskConfig:
391
+ """Create a dual-selector task configuration from URIs.
392
+
393
+ Args:
394
+ input1_uri: First input copick URI string.
395
+ input2_uri: Second input copick URI string.
396
+ input_type: Type of input objects (both inputs must be same type).
397
+ output_uri: Output copick URI string (supports smart defaults).
398
+ output_type: Type of output object.
399
+ pairing_method: How to pair inputs ("index_order", etc.).
400
+ individual_outputs: Whether to create individual outputs.
401
+ command_name: Name of the command (used for smart defaults in output_uri).
402
+
403
+ Returns:
404
+ TaskConfig instance ready for use.
405
+
406
+ Example:
407
+ config = create_dual_selector_config(
408
+ input1_uri="membrane:user1/manual-001",
409
+ input2_uri="vesicle:user1/auto-001",
410
+ input_type="mesh",
411
+ output_uri="combined",
412
+ output_type="mesh",
413
+ command_name="meshop",
414
+ )
415
+ """
416
+ from copick.util.uri import parse_copick_uri
417
+
418
+ # Parse both inputs
419
+ parse_copick_uri(input1_uri, input_type)
420
+ input2_params = parse_copick_uri(input2_uri, input_type)
421
+ parse_copick_uri(output_uri, output_type)
422
+
423
+ # Create first selector from URIs
424
+ selector1_config = SelectorConfig.from_uris(
425
+ input_uri=input1_uri,
426
+ input_type=input_type,
427
+ output_uri=output_uri,
428
+ output_type=output_type,
429
+ individual_outputs=individual_outputs,
430
+ command_name=command_name,
431
+ )
432
+
433
+ # Create second selector manually (output fields not used)
434
+ input2_object_name = input2_params.get("object_name") or input2_params.get("name")
435
+
436
+ selector2_dict = {
437
+ "input_type": input_type,
438
+ "output_type": output_type,
439
+ "input_object_name": input2_object_name,
440
+ "input_user_id": input2_params["user_id"],
441
+ "input_session_id": input2_params["session_id"],
442
+ "output_object_name": input2_object_name, # Not used
443
+ "output_user_id": input2_params["user_id"], # Not used
444
+ "output_session_id": input2_params["session_id"], # Not used
445
+ "individual_outputs": False,
446
+ }
447
+
448
+ # Add segmentation-specific fields if needed
449
+ if input_type == "segmentation":
450
+ seg_name = input2_params.get("name")
451
+ voxel_spacing = input2_params.get("voxel_spacing")
452
+
453
+ if isinstance(voxel_spacing, str) and voxel_spacing != "*":
454
+ voxel_spacing = float(voxel_spacing)
455
+
456
+ selector2_dict["segmentation_name"] = seg_name
457
+ selector2_dict["voxel_spacing"] = voxel_spacing
458
+
459
+ selector2_config = SelectorConfig(**selector2_dict)
460
+
461
+ return TaskConfig(
462
+ type="dual_selector",
463
+ selectors=[selector1_config, selector2_config],
464
+ pairing_method=pairing_method,
465
+ )
466
+
467
+
468
+ def create_multi_selector_config(
469
+ input_uris: List[str],
470
+ input_type: Literal["mesh", "segmentation"],
471
+ output_uri: str,
472
+ output_type: Literal["mesh", "segmentation"],
473
+ pairing_method: str = "n_way",
474
+ individual_outputs: bool = False,
475
+ command_name: Optional[str] = None,
476
+ ) -> TaskConfig:
477
+ """
478
+ Create a multi-selector task configuration from N input URIs.
479
+
480
+ Args:
481
+ input_uris: List of input copick URI strings (N≥2)
482
+ input_type: Type of input objects (all must be same type)
483
+ output_uri: Output copick URI string
484
+ output_type: Type of output object
485
+ pairing_method: How to pair inputs ("n_way")
486
+ individual_outputs: Whether to create individual outputs
487
+ command_name: Name of command for smart defaults
488
+
489
+ Returns:
490
+ TaskConfig with multi_selector type
491
+
492
+ Raises:
493
+ ValueError: If fewer than 2 input URIs provided
494
+
495
+ Example:
496
+ config = create_multi_selector_config(
497
+ input_uris=[
498
+ "membrane:user1/manual-*",
499
+ "vesicle:user2/auto-*",
500
+ "ribosome:user3/pred-*"
501
+ ],
502
+ input_type="segmentation",
503
+ output_uri="merged",
504
+ output_type="segmentation",
505
+ command_name="segop",
506
+ )
507
+ """
508
+ from copick.util.uri import parse_copick_uri
509
+
510
+ if len(input_uris) < 2:
511
+ raise ValueError(f"At least 2 input URIs required, got {len(input_uris)}")
512
+
513
+ # Create selector for first input (determines output config)
514
+ first_selector = SelectorConfig.from_uris(
515
+ input_uri=input_uris[0],
516
+ input_type=input_type,
517
+ output_uri=output_uri,
518
+ output_type=output_type,
519
+ individual_outputs=individual_outputs,
520
+ command_name=command_name,
521
+ )
522
+
523
+ selectors = [first_selector]
524
+
525
+ # Create selectors for remaining inputs (output fields unused)
526
+ for input_uri in input_uris[1:]:
527
+ params = parse_copick_uri(input_uri, input_type)
528
+ object_name = params.get("object_name") or params.get("name")
529
+
530
+ selector_dict = {
531
+ "input_type": input_type,
532
+ "output_type": output_type,
533
+ "input_object_name": object_name,
534
+ "input_user_id": params["user_id"],
535
+ "input_session_id": params["session_id"],
536
+ "output_object_name": object_name, # Not used
537
+ "output_user_id": params["user_id"], # Not used
538
+ "output_session_id": params["session_id"], # Not used
539
+ "individual_outputs": False,
540
+ }
541
+
542
+ # Add segmentation-specific fields
543
+ if input_type == "segmentation":
544
+ voxel_spacing = params.get("voxel_spacing")
545
+ if isinstance(voxel_spacing, str) and voxel_spacing != "*":
546
+ voxel_spacing = float(voxel_spacing)
547
+
548
+ selector_dict["segmentation_name"] = object_name
549
+ selector_dict["voxel_spacing"] = voxel_spacing
550
+
551
+ selectors.append(SelectorConfig(**selector_dict))
552
+
553
+ return TaskConfig(
554
+ type="multi_selector",
555
+ selectors=selectors,
556
+ pairing_method=pairing_method,
557
+ )
558
+
559
+
560
+ def create_reference_config(
561
+ input_uri: str,
562
+ input_type: Literal["picks", "mesh", "segmentation"],
563
+ output_uri: str,
564
+ output_type: Literal["picks", "mesh", "segmentation"],
565
+ reference_uri: str,
566
+ reference_type: Literal["mesh", "segmentation"],
567
+ additional_params: Optional[Dict[str, Any]] = None,
568
+ command_name: Optional[str] = None,
569
+ ) -> TaskConfig:
570
+ """Create a single-selector-with-reference task configuration from URIs.
571
+
572
+ Args:
573
+ input_uri: Input copick URI string.
574
+ input_type: Type of input object.
575
+ output_uri: Output copick URI string (supports smart defaults).
576
+ output_type: Type of output object.
577
+ reference_uri: Reference copick URI string.
578
+ reference_type: Type of reference object.
579
+ additional_params: Additional parameters for reference config.
580
+ command_name: Name of the command (used for smart defaults in output_uri).
581
+
582
+ Returns:
583
+ TaskConfig instance ready for use.
584
+
585
+ Example:
586
+ config = create_reference_config(
587
+ input_uri="ribosome:user1/all-001",
588
+ input_type="picks",
589
+ output_uri="ribosome",
590
+ output_type="picks",
591
+ reference_uri="boundary:user1/boundary-001",
592
+ reference_type="mesh",
593
+ command_name="picksin",
594
+ )
595
+ """
596
+ selector_config = SelectorConfig.from_uris(
597
+ input_uri=input_uri,
598
+ input_type=input_type,
599
+ output_uri=output_uri,
600
+ output_type=output_type,
601
+ command_name=command_name,
602
+ )
603
+
604
+ reference_config = ReferenceConfig.from_uri(
605
+ uri=reference_uri,
606
+ reference_type=reference_type,
607
+ additional_params=additional_params,
608
+ )
609
+
610
+ return TaskConfig(
611
+ type="single_selector_with_reference",
612
+ selector=selector_config,
613
+ reference=reference_config,
614
+ )
@@ -1,15 +1,25 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: copick-utils
3
- Version: 0.6.0
3
+ Version: 1.0.0
4
4
  Summary: Utilities for copick
5
- License: MIT
6
- Author: Kyle Harrington
7
- Author-email: czi@kyleharrington.com
8
- Requires-Python: >=3.9
5
+ Project-URL: Repository, https://github.com/KyleHarrington/copick-utils.git
6
+ Project-URL: Issues, https://github.com/KyleHarrington/copick-utils/issues
7
+ Project-URL: Documentation, https://github.com/KyleHarrington/copick-utils#readme
8
+ Author-email: Kyle Harrington <czi@kyleharrington.com>, Jonathan Schwartz <jonathan.schwartz@czii.org>
9
+ License: MIT License
10
+
11
+ Copyright (c) 2024-present Kyle Harrington <czi@kyleharrington.com>
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
+ License-File: LICENSE
19
+ Keywords: annotation,copick,cryo-et,cryoet,tomography,utilities
9
20
  Classifier: Development Status :: 4 - Beta
10
21
  Classifier: License :: OSI Approved :: MIT License
11
22
  Classifier: Programming Language :: Python
12
- Classifier: Programming Language :: Python :: 3
13
23
  Classifier: Programming Language :: Python :: 3.9
14
24
  Classifier: Programming Language :: Python :: 3.10
15
25
  Classifier: Programming Language :: Python :: 3.11
@@ -17,10 +27,27 @@ Classifier: Programming Language :: Python :: 3.12
17
27
  Classifier: Programming Language :: Python :: 3.13
18
28
  Classifier: Programming Language :: Python :: Implementation :: CPython
19
29
  Classifier: Programming Language :: Python :: Implementation :: PyPy
20
- Requires-Dist: copick (>=0.8.0)
21
- Project-URL: Documentation, https://github.com/KyleHarrington/copick-utils#readme
22
- Project-URL: Issues, https://github.com/KyleHarrington/copick-utils/issues
23
- Project-URL: Source, https://github.com/KyleHarrington/copick-utils
30
+ Requires-Python: >=3.9
31
+ Requires-Dist: click
32
+ Requires-Dist: click-option-group
33
+ Requires-Dist: copick>=1.16.0
34
+ Requires-Dist: manifold3d
35
+ Requires-Dist: mapbox-earcut
36
+ Requires-Dist: numpy
37
+ Requires-Dist: rtree
38
+ Requires-Dist: scikit-image
39
+ Requires-Dist: scikit-learn
40
+ Requires-Dist: scipy
41
+ Requires-Dist: shapely
42
+ Requires-Dist: tqdm
43
+ Requires-Dist: trimesh
44
+ Requires-Dist: zarr
45
+ Provides-Extra: dev
46
+ Requires-Dist: black>=25.1.0; extra == 'dev'
47
+ Requires-Dist: hatch-vcs>=0.4.0; extra == 'dev'
48
+ Requires-Dist: hatchling>=1.25.0; extra == 'dev'
49
+ Requires-Dist: pre-commit>=4.2.0; extra == 'dev'
50
+ Requires-Dist: ruff>=0.12.0; extra == 'dev'
24
51
  Description-Content-Type: text/markdown
25
52
 
26
53
  # copick-utils
@@ -69,4 +96,3 @@ This project adheres to the Contributor Covenant [code of conduct](https://githu
69
96
  ## Reporting Security Issues
70
97
 
71
98
  If you believe you have found a security issue, please responsibly disclose by contacting us at [security@chanzuckerberg.com](mailto:security@chanzuckerberg.com).
72
-