copick-utils 0.6.1__tar.gz → 1.0.0__tar.gz

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 (86) hide show
  1. {copick_utils-0.6.1 → copick_utils-1.0.0}/.github/workflows/conventional-commits.yml +1 -1
  2. {copick_utils-0.6.1 → copick_utils-1.0.0}/.github/workflows/py-formatting.yml +2 -2
  3. {copick_utils-0.6.1 → copick_utils-1.0.0}/.github/workflows/release-please.yml +2 -2
  4. copick_utils-1.0.0/.release-please.manifest.json +3 -0
  5. copick_utils-1.0.0/CHANGELOG.md +36 -0
  6. {copick_utils-0.6.1 → copick_utils-1.0.0}/PKG-INFO +15 -2
  7. {copick_utils-0.6.1 → copick_utils-1.0.0}/pyproject.toml +44 -1
  8. {copick_utils-0.6.1 → copick_utils-1.0.0}/src/copick_utils/__init__.py +1 -1
  9. copick_utils-1.0.0/src/copick_utils/cli/__init__.py +33 -0
  10. copick_utils-1.0.0/src/copick_utils/cli/clipmesh.py +161 -0
  11. copick_utils-1.0.0/src/copick_utils/cli/clippicks.py +154 -0
  12. copick_utils-1.0.0/src/copick_utils/cli/clipseg.py +163 -0
  13. copick_utils-1.0.0/src/copick_utils/cli/conversion_commands.py +32 -0
  14. copick_utils-1.0.0/src/copick_utils/cli/enclosed.py +191 -0
  15. copick_utils-1.0.0/src/copick_utils/cli/filter_components.py +166 -0
  16. copick_utils-1.0.0/src/copick_utils/cli/fit_spline.py +191 -0
  17. copick_utils-1.0.0/src/copick_utils/cli/hull.py +138 -0
  18. copick_utils-1.0.0/src/copick_utils/cli/input_output_selection.py +76 -0
  19. copick_utils-1.0.0/src/copick_utils/cli/logical_commands.py +29 -0
  20. copick_utils-1.0.0/src/copick_utils/cli/mesh2picks.py +170 -0
  21. copick_utils-1.0.0/src/copick_utils/cli/mesh2seg.py +167 -0
  22. copick_utils-1.0.0/src/copick_utils/cli/meshop.py +262 -0
  23. copick_utils-1.0.0/src/copick_utils/cli/picks2ellipsoid.py +171 -0
  24. copick_utils-1.0.0/src/copick_utils/cli/picks2mesh.py +181 -0
  25. copick_utils-1.0.0/src/copick_utils/cli/picks2plane.py +156 -0
  26. copick_utils-1.0.0/src/copick_utils/cli/picks2seg.py +134 -0
  27. copick_utils-1.0.0/src/copick_utils/cli/picks2sphere.py +170 -0
  28. copick_utils-1.0.0/src/copick_utils/cli/picks2surface.py +164 -0
  29. copick_utils-1.0.0/src/copick_utils/cli/picksin.py +146 -0
  30. copick_utils-1.0.0/src/copick_utils/cli/picksout.py +148 -0
  31. copick_utils-1.0.0/src/copick_utils/cli/processing_commands.py +18 -0
  32. copick_utils-1.0.0/src/copick_utils/cli/seg2mesh.py +135 -0
  33. copick_utils-1.0.0/src/copick_utils/cli/seg2picks.py +128 -0
  34. copick_utils-1.0.0/src/copick_utils/cli/segop.py +248 -0
  35. copick_utils-1.0.0/src/copick_utils/cli/separate_components.py +155 -0
  36. copick_utils-1.0.0/src/copick_utils/cli/skeletonize.py +164 -0
  37. copick_utils-1.0.0/src/copick_utils/cli/util.py +580 -0
  38. copick_utils-1.0.0/src/copick_utils/cli/validbox.py +155 -0
  39. copick_utils-1.0.0/src/copick_utils/converters/__init__.py +35 -0
  40. copick_utils-1.0.0/src/copick_utils/converters/converter_common.py +543 -0
  41. copick_utils-1.0.0/src/copick_utils/converters/ellipsoid_from_picks.py +335 -0
  42. copick_utils-1.0.0/src/copick_utils/converters/lazy_converter.py +576 -0
  43. copick_utils-1.0.0/src/copick_utils/converters/mesh_from_picks.py +209 -0
  44. copick_utils-1.0.0/src/copick_utils/converters/mesh_from_segmentation.py +119 -0
  45. copick_utils-1.0.0/src/copick_utils/converters/picks_from_mesh.py +542 -0
  46. copick_utils-1.0.0/src/copick_utils/converters/picks_from_segmentation.py +168 -0
  47. copick_utils-1.0.0/src/copick_utils/converters/plane_from_picks.py +251 -0
  48. copick_utils-1.0.0/src/copick_utils/converters/segmentation_from_mesh.py +291 -0
  49. {copick_utils-0.6.1/src/copick_utils/segmentation → copick_utils-1.0.0/src/copick_utils/converters}/segmentation_from_picks.py +123 -13
  50. copick_utils-1.0.0/src/copick_utils/converters/sphere_from_picks.py +306 -0
  51. copick_utils-1.0.0/src/copick_utils/converters/surface_from_picks.py +337 -0
  52. copick_utils-1.0.0/src/copick_utils/logical/__init__.py +43 -0
  53. copick_utils-1.0.0/src/copick_utils/logical/distance_operations.py +604 -0
  54. copick_utils-1.0.0/src/copick_utils/logical/enclosed_operations.py +222 -0
  55. copick_utils-1.0.0/src/copick_utils/logical/mesh_operations.py +443 -0
  56. copick_utils-1.0.0/src/copick_utils/logical/point_operations.py +303 -0
  57. copick_utils-1.0.0/src/copick_utils/logical/segmentation_operations.py +399 -0
  58. copick_utils-1.0.0/src/copick_utils/process/__init__.py +47 -0
  59. copick_utils-1.0.0/src/copick_utils/process/connected_components.py +360 -0
  60. copick_utils-1.0.0/src/copick_utils/process/filter_components.py +306 -0
  61. copick_utils-1.0.0/src/copick_utils/process/hull.py +106 -0
  62. copick_utils-1.0.0/src/copick_utils/process/skeletonize.py +326 -0
  63. copick_utils-1.0.0/src/copick_utils/process/spline_fitting.py +648 -0
  64. copick_utils-1.0.0/src/copick_utils/process/validbox.py +333 -0
  65. copick_utils-1.0.0/src/copick_utils/util/__init__.py +6 -0
  66. copick_utils-1.0.0/src/copick_utils/util/config_models.py +614 -0
  67. copick_utils-1.0.0/tests/__init__.py +15 -0
  68. copick_utils-0.6.1/.release-please.manifest.json +0 -3
  69. copick_utils-0.6.1/CHANGELOG.md +0 -15
  70. copick_utils-0.6.1/src/copick_utils/segmentation/picks_from_segmentation.py +0 -81
  71. copick_utils-0.6.1/tests/__init__.py +0 -3
  72. {copick_utils-0.6.1 → copick_utils-1.0.0}/.github/dependabot.yml +0 -0
  73. {copick_utils-0.6.1 → copick_utils-1.0.0}/.gitignore +0 -0
  74. {copick_utils-0.6.1 → copick_utils-1.0.0}/.pre-commit-config.yaml +0 -0
  75. {copick_utils-0.6.1 → copick_utils-1.0.0}/LICENSE +0 -0
  76. {copick_utils-0.6.1 → copick_utils-1.0.0}/README.md +0 -0
  77. {copick_utils-0.6.1 → copick_utils-1.0.0}/SECURITY.md +0 -0
  78. {copick_utils-0.6.1 → copick_utils-1.0.0}/examples/segmentation_example.ipynb +0 -0
  79. {copick_utils-0.6.1 → copick_utils-1.0.0}/release-please.config.json +0 -0
  80. {copick_utils-0.6.1 → copick_utils-1.0.0}/src/copick_utils/features/__init__.py +0 -0
  81. {copick_utils-0.6.1 → copick_utils-1.0.0}/src/copick_utils/features/skimage.py +0 -0
  82. {copick_utils-0.6.1/src/copick_utils/pickers → copick_utils-1.0.0/src/copick_utils/io}/__init__.py +0 -0
  83. {copick_utils-0.6.1 → copick_utils-1.0.0}/src/copick_utils/io/readers.py +0 -0
  84. {copick_utils-0.6.1 → copick_utils-1.0.0}/src/copick_utils/io/writers.py +0 -0
  85. {copick_utils-0.6.1/src/copick_utils/segmentation → copick_utils-1.0.0/src/copick_utils/pickers}/__init__.py +0 -0
  86. {copick_utils-0.6.1 → copick_utils-1.0.0}/src/copick_utils/pickers/grid_picker.py +0 -0
@@ -16,4 +16,4 @@ jobs:
16
16
  conventional_commit_title:
17
17
  runs-on: ubuntu-latest
18
18
  steps:
19
- - uses: chanzuckerberg/github-actions/.github/actions/conventional-commits@v6.4.0
19
+ - uses: chanzuckerberg/github-actions/.github/actions/conventional-commits@v6.10.0
@@ -10,8 +10,8 @@ jobs:
10
10
  name: pre-commit checks
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v4
14
- - uses: actions/setup-python@v5
13
+ - uses: actions/checkout@v5
14
+ - uses: actions/setup-python@v6
15
15
  with:
16
16
  python-version: "3.12.5"
17
17
  - name: Install action-validator with asdf
@@ -43,13 +43,13 @@ jobs:
43
43
  id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
44
44
  steps:
45
45
  - name: Checkout ref branch
46
- uses: actions/checkout@v4
46
+ uses: actions/checkout@v5
47
47
  with:
48
48
  ref: ${{ github.ref }}
49
49
  fetch-depth: 0
50
50
 
51
51
  - name: Install uv
52
- uses: astral-sh/setup-uv@v6
52
+ uses: astral-sh/setup-uv@v7
53
53
  with:
54
54
  version: "0.7.13"
55
55
  python-version: "3.12"
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.0.0"
3
+ }
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ ## [1.0.0](https://github.com/copick/copick-utils/compare/copick-utils-v0.6.1...copick-utils-v1.0.0) (2025-10-16)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * Conversion, logical and processing commands for copick-utils CLI ([#24](https://github.com/copick/copick-utils/issues/24))
9
+
10
+ ### ✨ Features
11
+
12
+ * Conversion, logical and processing commands for copick-utils CLI ([#24](https://github.com/copick/copick-utils/issues/24)) ([d455fd2](https://github.com/copick/copick-utils/commit/d455fd217f4cd4edf18cfe8da48d951799fa272a))
13
+
14
+
15
+ ### 🐞 Bug Fixes
16
+
17
+ * bump actions/checkout from 4 to 5 ([#22](https://github.com/copick/copick-utils/issues/22)) ([287ac56](https://github.com/copick/copick-utils/commit/287ac566545d5c8b496dfdc0ae7e18456337de52))
18
+ * bump actions/setup-python from 5 to 6 ([#25](https://github.com/copick/copick-utils/issues/25)) ([f3ed071](https://github.com/copick/copick-utils/commit/f3ed071dafa9469daee9f62a9a06319de4c76d63))
19
+ * bump astral-sh/setup-uv from 6 to 7 ([#30](https://github.com/copick/copick-utils/issues/30)) ([b3028ef](https://github.com/copick/copick-utils/commit/b3028ef908f0e0d42cf418abb5169ab3a87556eb))
20
+ * bump chanzuckerberg/github-actions from 6.4.0 to 6.5.0 ([#20](https://github.com/copick/copick-utils/issues/20)) ([13056ae](https://github.com/copick/copick-utils/commit/13056ae6ee0fc0132c20b5f0539614c9d4d4aa24))
21
+ * bump chanzuckerberg/github-actions from 6.5.0 to 6.6.0 ([#23](https://github.com/copick/copick-utils/issues/23)) ([2c6a994](https://github.com/copick/copick-utils/commit/2c6a99497098073cecb52b5094da9f6ede040c4a))
22
+ * bump chanzuckerberg/github-actions from 6.6.0 to 6.10.0 ([#29](https://github.com/copick/copick-utils/issues/29)) ([bb50f1e](https://github.com/copick/copick-utils/commit/bb50f1ee9070cf33cee6d78e64aefe5063e5f0eb))
23
+
24
+ ## [0.6.1](https://github.com/copick/copick-utils/compare/copick-utils-v0.6.0...copick-utils-v0.6.1) (2025-07-16)
25
+
26
+
27
+ ### 🐞 Bug Fixes
28
+
29
+ * bump actions/checkout from 3 to 4 ([#19](https://github.com/copick/copick-utils/issues/19)) ([570072f](https://github.com/copick/copick-utils/commit/570072f708ea47b54d00fcc24451401896c91e25))
30
+ * bump actions/setup-python from 4 to 5 ([#17](https://github.com/copick/copick-utils/issues/17)) ([95c4784](https://github.com/copick/copick-utils/commit/95c47842e6a271ae40f69a9f9a578ef55d3d318b))
31
+ * bump asdf-vm/actions from 3 to 4 ([#18](https://github.com/copick/copick-utils/issues/18)) ([039b576](https://github.com/copick/copick-utils/commit/039b5761c4bc6b128f1bfef1aa2e7bf612f42ea3))
32
+
33
+
34
+ ### 🧹 Miscellaneous Chores
35
+
36
+ * Set up CI for copick-utils ([#14](https://github.com/copick/copick-utils/issues/14)) ([0fbcfbb](https://github.com/copick/copick-utils/commit/0fbcfbb5bc12261da52e0850531195049328c2f9))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: copick-utils
3
- Version: 0.6.1
3
+ Version: 1.0.0
4
4
  Summary: Utilities for copick
5
5
  Project-URL: Repository, https://github.com/KyleHarrington/copick-utils.git
6
6
  Project-URL: Issues, https://github.com/KyleHarrington/copick-utils/issues
@@ -28,7 +28,20 @@ Classifier: Programming Language :: Python :: 3.13
28
28
  Classifier: Programming Language :: Python :: Implementation :: CPython
29
29
  Classifier: Programming Language :: Python :: Implementation :: PyPy
30
30
  Requires-Python: >=3.9
31
- Requires-Dist: copick>=0.8.0
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
32
45
  Provides-Extra: dev
33
46
  Requires-Dist: black>=25.1.0; extra == 'dev'
34
47
  Requires-Dist: hatch-vcs>=0.4.0; extra == 'dev'
@@ -19,7 +19,20 @@ classifiers = [
19
19
  ]
20
20
  dynamic = ["version"]
21
21
  dependencies = [
22
- "copick>=0.8.0",
22
+ "copick>=1.16.0",
23
+ "click",
24
+ "click-option-group",
25
+ "numpy",
26
+ "scipy",
27
+ "scikit-image",
28
+ "zarr",
29
+ "trimesh",
30
+ "manifold3d",
31
+ "mapbox-earcut",
32
+ "tqdm",
33
+ "scikit-learn",
34
+ "shapely",
35
+ "rtree"
23
36
  ]
24
37
  authors = [
25
38
  { name = "Kyle Harrington", email = "czi@kyleharrington.com" },
@@ -44,6 +57,36 @@ dev = [
44
57
  "ruff>=0.12.0",
45
58
  ]
46
59
 
60
+ [project.entry-points."copick.convert.commands"]
61
+ picks2seg = "copick_utils.cli.conversion_commands:picks2seg"
62
+ seg2picks = "copick_utils.cli.conversion_commands:seg2picks"
63
+ mesh2seg = "copick_utils.cli.conversion_commands:mesh2seg"
64
+ seg2mesh = "copick_utils.cli.conversion_commands:seg2mesh"
65
+ mesh2picks = "copick_utils.cli.conversion_commands:mesh2picks"
66
+ picks2mesh = "copick_utils.cli.conversion_commands:picks2mesh"
67
+ picks2sphere = "copick_utils.cli.conversion_commands:picks2sphere"
68
+ picks2ellipsoid = "copick_utils.cli.conversion_commands:picks2ellipsoid"
69
+ picks2plane = "copick_utils.cli.conversion_commands:picks2plane"
70
+ picks2surface = "copick_utils.cli.conversion_commands:picks2surface"
71
+
72
+ [project.entry-points."copick.process.commands"]
73
+ separate_components = "copick_utils.cli.processing_commands:separate_components"
74
+ filter_components = "copick_utils.cli.processing_commands:filter_components"
75
+ skeletonize = "copick_utils.cli.processing_commands:skeletonize"
76
+ fit_spline = "copick_utils.cli.processing_commands:fit_spline"
77
+ validbox = "copick_utils.cli.processing_commands:validbox"
78
+ hull = "copick_utils.cli.processing_commands:hull"
79
+
80
+ [project.entry-points."copick.logical.commands"]
81
+ meshop = "copick_utils.cli.logical_commands:meshop"
82
+ segop = "copick_utils.cli.logical_commands:segop"
83
+ enclosed = "copick_utils.cli.logical_commands:enclosed"
84
+ clipmesh = "copick_utils.cli.logical_commands:clipmesh"
85
+ clipseg = "copick_utils.cli.logical_commands:clipseg"
86
+ clippicks = "copick_utils.cli.logical_commands:clippicks"
87
+ picksin = "copick_utils.cli.logical_commands:picksin"
88
+ picksout = "copick_utils.cli.logical_commands:picksout"
89
+
47
90
  [tool.hatch.version]
48
91
  path = "src/copick_utils/__init__.py"
49
92
 
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2024-present Kyle Harrington <czi@kyleharrington.com>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.6.1"
4
+ __version__ = "1.0.0"
@@ -0,0 +1,33 @@
1
+ """CLI commands for copick-utils."""
2
+
3
+ from .conversion_commands import (
4
+ mesh2picks,
5
+ mesh2seg,
6
+ picks2ellipsoid,
7
+ picks2mesh,
8
+ picks2plane,
9
+ picks2seg,
10
+ picks2sphere,
11
+ picks2surface,
12
+ seg2mesh,
13
+ seg2picks,
14
+ )
15
+ from .processing_commands import fit_spline, separate_components, skeletonize
16
+
17
+ __all__ = [
18
+ # Conversion commands
19
+ "picks2seg",
20
+ "seg2picks",
21
+ "mesh2seg",
22
+ "seg2mesh",
23
+ "picks2mesh",
24
+ "mesh2picks",
25
+ "picks2surface",
26
+ "picks2plane",
27
+ "picks2sphere",
28
+ "picks2ellipsoid",
29
+ # Processing commands
30
+ "separate_components",
31
+ "skeletonize",
32
+ "fit_spline",
33
+ ]
@@ -0,0 +1,161 @@
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_distance_options,
10
+ add_input_option,
11
+ add_output_option,
12
+ add_reference_mesh_option,
13
+ add_reference_seg_option,
14
+ add_workers_option,
15
+ )
16
+ from copick_utils.util.config_models import create_reference_config
17
+
18
+
19
+ @click.command(
20
+ context_settings={"show_default": True},
21
+ short_help="Limit meshes to vertices within distance of a reference surface.",
22
+ no_args_is_help=True,
23
+ )
24
+ @add_config_option
25
+ @optgroup.group("\nInput Options", help="Options related to the input meshes.")
26
+ @optgroup.option(
27
+ "--run-names",
28
+ "-r",
29
+ multiple=True,
30
+ help="Specific run names to process (default: all runs).",
31
+ )
32
+ @add_input_option("mesh")
33
+ @optgroup.group("\nReference Options", help="Options for reference surface (provide either mesh or segmentation).")
34
+ @add_reference_mesh_option(required=False)
35
+ @add_reference_seg_option(required=False)
36
+ @optgroup.group("\nTool Options", help="Options related to this tool.")
37
+ @add_distance_options
38
+ @add_workers_option
39
+ @optgroup.group("\nOutput Options", help="Options related to output meshes.")
40
+ @add_output_option("mesh", default_tool="clipmesh")
41
+ @optgroup.option(
42
+ "--individual-meshes/--no-individual-meshes",
43
+ "-im",
44
+ is_flag=True,
45
+ default=False,
46
+ help="Create individual meshes for each instance (enables {instance_id} placeholder).",
47
+ )
48
+ @add_debug_option
49
+ def clipmesh(
50
+ config,
51
+ run_names,
52
+ input_uri,
53
+ ref_mesh_uri,
54
+ ref_seg_uri,
55
+ max_distance,
56
+ mesh_voxel_spacing,
57
+ workers,
58
+ output_uri,
59
+ individual_meshes,
60
+ debug,
61
+ ):
62
+ """
63
+ Limit meshes to vertices within a certain distance of a reference surface.
64
+
65
+ \b
66
+ URI Format:
67
+ Meshes: object_name:user_id/session_id
68
+ Segmentations: name:user_id/session_id@voxel_spacing
69
+
70
+ \b
71
+ The reference surface can be either a mesh or a segmentation.
72
+ Only mesh vertices within the specified distance will be kept.
73
+
74
+ \b
75
+ Examples:
76
+ # Limit mesh to vertices near reference mesh surface
77
+ copick logical clipmesh -i "membrane:user1/full-001" -rm "boundary:user1/boundary-001" -o "membrane:clipmesh/limited-001" --max-distance 50.0
78
+
79
+ # Limit using segmentation as reference
80
+ copick logical clipmesh -i "membrane:user1/full-001" -rs "mask:user1/mask-001@10.0" -o "membrane:clipmesh/limited-001" --max-distance 100.0
81
+ """
82
+ from copick_utils.logical.distance_operations import limit_mesh_by_distance_lazy_batch
83
+
84
+ logger = get_logger(__name__, debug=debug)
85
+
86
+ # Validate that exactly one reference type is provided
87
+ if not ref_mesh_uri and not ref_seg_uri:
88
+ raise click.BadParameter("Must provide either --ref-mesh or --ref-seg")
89
+ if ref_mesh_uri and ref_seg_uri:
90
+ raise click.BadParameter("Cannot provide both --ref-mesh and --ref-seg")
91
+
92
+ root = copick.from_file(config)
93
+ run_names_list = list(run_names) if run_names else None
94
+
95
+ # Determine reference type and URI
96
+ reference_uri = ref_mesh_uri or ref_seg_uri
97
+ reference_type = "mesh" if ref_mesh_uri else "segmentation"
98
+
99
+ # Create config directly from URIs with additional params for distance operations
100
+ try:
101
+ task_config = create_reference_config(
102
+ input_uri=input_uri,
103
+ input_type="mesh",
104
+ output_uri=output_uri,
105
+ output_type="mesh",
106
+ reference_uri=reference_uri,
107
+ reference_type=reference_type,
108
+ additional_params={"max_distance": max_distance, "mesh_voxel_spacing": mesh_voxel_spacing},
109
+ command_name="clipmesh",
110
+ )
111
+ except ValueError as e:
112
+ raise click.BadParameter(str(e)) from e
113
+
114
+ # Extract parameters for logging
115
+ input_params = parse_copick_uri(input_uri, "mesh")
116
+ output_params = parse_copick_uri(output_uri, "mesh")
117
+ ref_params = parse_copick_uri(reference_uri, reference_type)
118
+
119
+ logger.info(f"Limiting meshes by distance for object '{input_params['object_name']}'")
120
+ logger.info(f"Source mesh pattern: {input_params['user_id']}/{input_params['session_id']}")
121
+ if reference_type == "mesh":
122
+ logger.info(f"Reference mesh: {ref_params['object_name']} ({ref_params['user_id']}/{ref_params['session_id']})")
123
+ else:
124
+ logger.info(
125
+ f"Reference segmentation: {ref_params['name']} ({ref_params['user_id']}/{ref_params['session_id']})",
126
+ )
127
+ logger.info(f"Maximum distance: {max_distance} angstroms")
128
+ logger.info(
129
+ f"Target mesh template: {output_params['object_name']} ({output_params['user_id']}/{output_params['session_id']})",
130
+ )
131
+
132
+ # Parallel discovery and processing - no sequential bottleneck!
133
+ results = limit_mesh_by_distance_lazy_batch(
134
+ root=root,
135
+ config=task_config,
136
+ run_names=run_names_list,
137
+ workers=workers,
138
+ )
139
+
140
+ successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
141
+ total_vertices = sum(result.get("vertices_created", 0) for result in results.values() if result)
142
+ total_faces = sum(result.get("faces_created", 0) for result in results.values() if result)
143
+ total_processed = sum(result.get("processed", 0) for result in results.values() if result)
144
+
145
+ # Collect all errors
146
+ all_errors = []
147
+ for result in results.values():
148
+ if result and result.get("errors"):
149
+ all_errors.extend(result["errors"])
150
+
151
+ logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
152
+ logger.info(f"Total distance limiting operations completed: {total_processed}")
153
+ logger.info(f"Total vertices created: {total_vertices}")
154
+ logger.info(f"Total faces created: {total_faces}")
155
+
156
+ if all_errors:
157
+ logger.warning(f"Encountered {len(all_errors)} errors during processing")
158
+ for error in all_errors[:5]: # Show first 5 errors
159
+ logger.warning(f" - {error}")
160
+ if len(all_errors) > 5:
161
+ logger.warning(f" ... and {len(all_errors) - 5} more errors")
@@ -0,0 +1,154 @@
1
+ """CLI commands for distance-based limiting operations."""
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_distance_options,
12
+ add_input_option,
13
+ add_output_option,
14
+ add_reference_mesh_option,
15
+ add_reference_seg_option,
16
+ add_workers_option,
17
+ )
18
+ from copick_utils.util.config_models import create_reference_config
19
+
20
+
21
+ @click.command(
22
+ context_settings={"show_default": True},
23
+ short_help="Limit picks to those within distance of a reference surface.",
24
+ no_args_is_help=True,
25
+ )
26
+ @add_config_option
27
+ @optgroup.group("\nInput Options", help="Options related to the input picks.")
28
+ @optgroup.option(
29
+ "--run-names",
30
+ "-r",
31
+ multiple=True,
32
+ help="Specific run names to process (default: all runs).",
33
+ )
34
+ @add_input_option("picks")
35
+ @optgroup.group("\nReference Options", help="Options for reference surface (provide either mesh or segmentation).")
36
+ @add_reference_mesh_option(required=False)
37
+ @add_reference_seg_option(required=False)
38
+ @optgroup.group("\nTool Options", help="Options related to this tool.")
39
+ @add_distance_options
40
+ @add_workers_option
41
+ @optgroup.group("\nOutput Options", help="Options related to output picks.")
42
+ @add_output_option("picks", default_tool="clippicks")
43
+ @add_debug_option
44
+ def clippicks(
45
+ config,
46
+ run_names,
47
+ input_uri,
48
+ ref_mesh_uri,
49
+ ref_seg_uri,
50
+ max_distance,
51
+ mesh_voxel_spacing,
52
+ workers,
53
+ output_uri,
54
+ debug,
55
+ ):
56
+ """
57
+ Limit picks to those within a certain distance of a reference surface.
58
+
59
+ \b
60
+ URI Format:
61
+ Picks: object_name:user_id/session_id
62
+ Meshes: object_name:user_id/session_id
63
+ Segmentations: name:user_id/session_id@voxel_spacing
64
+
65
+ \b
66
+ The reference surface can be either a mesh or a segmentation.
67
+ Only picks within the specified distance will be kept.
68
+
69
+ \b
70
+ Examples:
71
+ # Limit picks to those near reference mesh surface
72
+ copick logical clippicks -i "ribosome:user1/all-001" -rm "boundary:user1/boundary-001" -o "ribosome:clippicks/limited-001" --max-distance 50.0
73
+
74
+ # Limit using segmentation as reference
75
+ copick logical clippicks -i "ribosome:user1/all-001" -rs "mask:user1/mask-001@10.0" -o "ribosome:clippicks/limited-001" --max-distance 100.0
76
+ """
77
+ from copick_utils.logical.distance_operations import limit_picks_by_distance_lazy_batch
78
+
79
+ logger = get_logger(__name__, debug=debug)
80
+
81
+ # Validate that exactly one reference type is provided
82
+ if not ref_mesh_uri and not ref_seg_uri:
83
+ raise click.BadParameter("Must provide either --ref-mesh or --ref-seg")
84
+ if ref_mesh_uri and ref_seg_uri:
85
+ raise click.BadParameter("Cannot provide both --ref-mesh and --ref-seg")
86
+
87
+ root = copick.from_file(config)
88
+ run_names_list = list(run_names) if run_names else None
89
+
90
+ # Determine reference type and URI
91
+ reference_uri = ref_mesh_uri or ref_seg_uri
92
+ reference_type = "mesh" if ref_mesh_uri else "segmentation"
93
+
94
+ # Create config directly from URIs with additional params
95
+ try:
96
+ task_config = create_reference_config(
97
+ input_uri=input_uri,
98
+ input_type="picks",
99
+ output_uri=output_uri,
100
+ output_type="picks",
101
+ reference_uri=reference_uri,
102
+ reference_type=reference_type,
103
+ additional_params={"max_distance": max_distance, "mesh_voxel_spacing": mesh_voxel_spacing},
104
+ command_name="clippicks",
105
+ )
106
+ except ValueError as e:
107
+ raise click.BadParameter(str(e)) from e
108
+
109
+ # Extract parameters for logging
110
+ input_params = parse_copick_uri(input_uri, "picks")
111
+ output_params = parse_copick_uri(output_uri, "picks")
112
+ ref_params = parse_copick_uri(reference_uri, reference_type)
113
+
114
+ logger.info(f"Limiting picks by distance for object '{input_params['object_name']}'")
115
+ logger.info(f"Source picks pattern: {input_params['user_id']}/{input_params['session_id']}")
116
+ if reference_type == "mesh":
117
+ logger.info(f"Reference mesh: {ref_params['object_name']} ({ref_params['user_id']}/{ref_params['session_id']})")
118
+ else:
119
+ logger.info(
120
+ f"Reference segmentation: {ref_params['name']} ({ref_params['user_id']}/{ref_params['session_id']})",
121
+ )
122
+ logger.info(f"Maximum distance: {max_distance} angstroms")
123
+ logger.info(
124
+ f"Target picks template: {output_params['object_name']} ({output_params['user_id']}/{output_params['session_id']})",
125
+ )
126
+
127
+ # Parallel discovery and processing - no sequential bottleneck!
128
+ results = limit_picks_by_distance_lazy_batch(
129
+ root=root,
130
+ config=task_config,
131
+ run_names=run_names_list,
132
+ workers=workers,
133
+ )
134
+
135
+ successful = sum(1 for result in results.values() if result and result.get("processed", 0) > 0)
136
+ total_points = sum(result.get("points_created", 0) for result in results.values() if result)
137
+ total_processed = sum(result.get("processed", 0) for result in results.values() if result)
138
+
139
+ # Collect all errors
140
+ all_errors = []
141
+ for result in results.values():
142
+ if result and result.get("errors"):
143
+ all_errors.extend(result["errors"])
144
+
145
+ logger.info(f"Completed: {successful}/{len(results)} runs processed successfully")
146
+ logger.info(f"Total distance limiting operations completed: {total_processed}")
147
+ logger.info(f"Total points created: {total_points}")
148
+
149
+ if all_errors:
150
+ logger.warning(f"Encountered {len(all_errors)} errors during processing")
151
+ for error in all_errors[:5]: # Show first 5 errors
152
+ logger.warning(f" - {error}")
153
+ if len(all_errors) > 5:
154
+ logger.warning(f" ... and {len(all_errors) - 5} more errors")