nps-cli 0.1.1__tar.gz → 0.1.3__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.
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nps-cli
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: A simple CLI for sampling point clouds from large volumetric datasets
5
5
  Author: Jakob Troidl
6
6
  License-Expression: MIT
7
- Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.11
8
8
  Requires-Python: >=3.11
9
9
  Description-Content-Type: text/markdown
10
10
  Requires-Dist: click
@@ -17,7 +17,10 @@ Requires-Dist: build; extra == "dev"
17
17
  Requires-Dist: twine; extra == "dev"
18
18
  Requires-Dist: ruff; extra == "dev"
19
19
 
20
- # CLI for Point Cloud Sampling
20
+ [![pypi](https://badge.fury.io/py/nps-cli.svg)](https://badge.fury.io/py/nps-cli)
21
+ [![Python Version](https://img.shields.io/pypi/pyversions/nps-cli.svg)](https://pypi.org/project/nps-cli/)
22
+
23
+ # CLI for Distributed Point Cloud Sampling
21
24
 
22
25
  ```
23
26
  pip install nps-cli
@@ -34,7 +37,7 @@ Options:
34
37
  --timestamp INTEGER Optional timestamp for the dataset version
35
38
  (graphene only).
36
39
  --sample_svids Sample SVIDs in addition to points (default:
37
- False).
40
+ False) Graphene only.
38
41
  -o, --output-dir DIRECTORY Output directory. [default: ./nps_output]
39
42
  --worker-type [LocalWorker|LSFWorker|SlurmWorker]
40
43
  Type of worker to use for sampling.
@@ -46,6 +49,8 @@ Options:
46
49
  local]
47
50
  --fraction FLOAT Fraction of points to sample [0.0, 1.0].
48
51
  [default: 0.001]
52
+ --bbox INTEGER... Bounding box: begin_x begin_y begin_z
53
+ end_x end_y end_z (in voxels).
49
54
  --block-size INTEGER... Block size in voxels (X Y Z). [default:
50
55
  128, 128, 128]
51
56
  -h, --help Show this message and exit.
@@ -57,6 +62,12 @@ Options:
57
62
  nps --cv-path precomputed://gs://neuroglancer-janelia-flyem-hemibrain/v1.0/segmentation
58
63
  ```
59
64
 
65
+ Sample point clouds within a FlyEM Hemibrain subvolume:
66
+
67
+ ```bash
68
+ nps --cv-path precomputed://gs://neuroglancer-janelia-flyem-hemibrain/v1.0/segmentation --bbox 15347 19712 18606 15859 20224 19118 --fraction 0.01
69
+ ```
70
+
60
71
  ### Reading Point Clouds
61
72
 
62
73
  Please refer to the [pocaduck](https://github.com/JaneliaSciComp/pocaduck?tab=readme-ov-file#querying-point-clouds) repo on how to read point clouds from the output directory:
@@ -80,3 +91,10 @@ query.close()
80
91
  ```
81
92
 
82
93
  For optimized point cloud reading, consider [this](https://github.com/JaneliaSciComp/pocaduck?tab=readme-ov-file#running-the-optimization-pipeline).
94
+
95
+
96
+ ### Deploy
97
+ ```python
98
+ python -m build
99
+ twine upload dist/*
100
+ ```
@@ -1,4 +1,7 @@
1
- # CLI for Point Cloud Sampling
1
+ [![pypi](https://badge.fury.io/py/nps-cli.svg)](https://badge.fury.io/py/nps-cli)
2
+ [![Python Version](https://img.shields.io/pypi/pyversions/nps-cli.svg)](https://pypi.org/project/nps-cli/)
3
+
4
+ # CLI for Distributed Point Cloud Sampling
2
5
 
3
6
  ```
4
7
  pip install nps-cli
@@ -15,7 +18,7 @@ Options:
15
18
  --timestamp INTEGER Optional timestamp for the dataset version
16
19
  (graphene only).
17
20
  --sample_svids Sample SVIDs in addition to points (default:
18
- False).
21
+ False) Graphene only.
19
22
  -o, --output-dir DIRECTORY Output directory. [default: ./nps_output]
20
23
  --worker-type [LocalWorker|LSFWorker|SlurmWorker]
21
24
  Type of worker to use for sampling.
@@ -27,6 +30,8 @@ Options:
27
30
  local]
28
31
  --fraction FLOAT Fraction of points to sample [0.0, 1.0].
29
32
  [default: 0.001]
33
+ --bbox INTEGER... Bounding box: begin_x begin_y begin_z
34
+ end_x end_y end_z (in voxels).
30
35
  --block-size INTEGER... Block size in voxels (X Y Z). [default:
31
36
  128, 128, 128]
32
37
  -h, --help Show this message and exit.
@@ -38,6 +43,12 @@ Options:
38
43
  nps --cv-path precomputed://gs://neuroglancer-janelia-flyem-hemibrain/v1.0/segmentation
39
44
  ```
40
45
 
46
+ Sample point clouds within a FlyEM Hemibrain subvolume:
47
+
48
+ ```bash
49
+ nps --cv-path precomputed://gs://neuroglancer-janelia-flyem-hemibrain/v1.0/segmentation --bbox 15347 19712 18606 15859 20224 19118 --fraction 0.01
50
+ ```
51
+
41
52
  ### Reading Point Clouds
42
53
 
43
54
  Please refer to the [pocaduck](https://github.com/JaneliaSciComp/pocaduck?tab=readme-ov-file#querying-point-clouds) repo on how to read point clouds from the output directory:
@@ -60,4 +71,11 @@ print(f"Retrieved {points.shape[0]} points for label 12345")
60
71
  query.close()
61
72
  ```
62
73
 
63
- For optimized point cloud reading, consider [this](https://github.com/JaneliaSciComp/pocaduck?tab=readme-ov-file#running-the-optimization-pipeline).
74
+ For optimized point cloud reading, consider [this](https://github.com/JaneliaSciComp/pocaduck?tab=readme-ov-file#running-the-optimization-pipeline).
75
+
76
+
77
+ ### Deploy
78
+ ```python
79
+ python -m build
80
+ twine upload dist/*
81
+ ```
@@ -0,0 +1 @@
1
+ from .sample_points import SamplePoints as SamplePoints
@@ -0,0 +1,132 @@
1
+ from contextlib import contextmanager
2
+ from typing import Literal
3
+
4
+ import numpy as np
5
+ import os
6
+ import logging
7
+ import daisy
8
+
9
+ from funlib.geometry import Coordinate, Roi
10
+ from volara.blockwise import BlockwiseTask
11
+ from volara.datasets import Dataset, CloudVolumeWrapper
12
+ from volara.utils import PydanticCoordinate
13
+ from daisy import Block
14
+ from pocaduck import StorageConfig, Ingestor
15
+
16
+
17
+ class SamplePoints(BlockwiseTask):
18
+ task_type: Literal["sample_pc"] = "sample_pc"
19
+ out_dir: str
20
+ labels: CloudVolumeWrapper
21
+ svids: CloudVolumeWrapper | None = None
22
+ block_size: PydanticCoordinate
23
+ fraction: float
24
+ fit: Literal["shrink"] = "shrink"
25
+ read_write_conflict: Literal[False] = False
26
+
27
+ @property
28
+ def task_name(self) -> str:
29
+ return f"{self.labels.name}-{self.task_type}"
30
+
31
+ @property
32
+ def write_roi(self) -> Roi:
33
+ total_roi = self.labels.array("r").roi
34
+ if self.roi is not None:
35
+ total_roi = total_roi.intersect(self.roi)
36
+ return total_roi
37
+
38
+
39
+ @property
40
+ def voxel_size(self) -> Coordinate:
41
+ return self.labels.voxel_size
42
+
43
+ @property
44
+ def write_size(self) -> Coordinate:
45
+ return self.block_size
46
+
47
+ @property
48
+ def context_size(self) -> Coordinate:
49
+ return Coordinate((0,) * self.write_size.dims)
50
+
51
+ def drop_artifacts(self):
52
+ pass
53
+
54
+ @property
55
+ def output_datasets(self) -> list[Dataset]:
56
+ return []
57
+
58
+ def sample_pc_in_block(self, block: Block, labels: np.ndarray, svids: np.ndarray | None, offset: Coordinate):
59
+ block_id = block.block_id[1]
60
+ try:
61
+ context = daisy.Context.from_env()
62
+ worker_id = context["worker_id"]
63
+ except KeyError:
64
+ worker_id = 0
65
+
66
+ logging.info(f"got {len(np.unique(labels))} in {block_id}")
67
+ storage_config = StorageConfig(base_path=self.out_dir)
68
+ ingestor = Ingestor(storage_config, worker_id=worker_id)
69
+ sampled_points = self.sample_segment_points(labels, self.fraction)
70
+
71
+ for seg, pts in sampled_points.items():
72
+ packed_pts = pts + offset
73
+ packed_pts = packed_pts.astype(np.uint64)
74
+
75
+ if self.svids is not None:
76
+ x, y, z = pts[:, 0], pts[:, 1], pts[:, 2]
77
+ sampled_svids = svids[x, y, z].reshape(-1, 1)
78
+ packed_pts = np.concatenate((packed_pts, sampled_svids), axis=1)
79
+
80
+ ingestor.write(label=seg, block_id=block_id, points=packed_pts)
81
+ ingestor.finalize()
82
+
83
+ def init(self):
84
+ os.makedirs(self.out_dir, exist_ok=True)
85
+
86
+ @contextmanager
87
+ def process_block_func(self):
88
+ data = self.labels.array("r")
89
+ if self.svids is not None:
90
+ supervoxels = self.svids.array("r")
91
+
92
+ def process_block(block: Block):
93
+ labels = data[block.write_roi.to_slices()]
94
+ labels = np.array(labels).squeeze()
95
+
96
+ offset = block.write_roi.get_begin()
97
+
98
+ if self.svids is not None:
99
+ s = supervoxels[block.write_roi.to_slices()]
100
+ s = np.array(s).squeeze()
101
+ else:
102
+ s = None
103
+
104
+ self.sample_pc_in_block(block, labels, s, offset)
105
+
106
+ yield process_block
107
+
108
+ storage_config = StorageConfig(base_path=self.out_dir)
109
+ Ingestor.consolidate_indexes(storage_config)
110
+
111
+ def sample_segment_points(self, labels, fraction, background=0, replace=False):
112
+ segment_ids = np.unique(labels)
113
+ if background is not None:
114
+ segment_ids = segment_ids[segment_ids != background]
115
+
116
+ segment_points = {}
117
+
118
+ for seg in segment_ids:
119
+ coords = np.column_stack(np.where(labels == seg))
120
+ num_points = coords.shape[0]
121
+
122
+ k = max(1, int(num_points * fraction))
123
+
124
+ if num_points > k:
125
+ selected_idx = np.random.choice(num_points, size=k, replace=replace)
126
+ sampled = coords[selected_idx]
127
+ else:
128
+ sampled = coords
129
+
130
+ segment_points[str(seg)] = sampled
131
+
132
+ return segment_points
@@ -19,7 +19,10 @@ from volara.workers import LSFWorker, LocalWorker, SlurmWorker
19
19
  @click.option('--queue', default='local', show_default=True, help='Queue name (for LSF backend).')
20
20
  @click.option('--fraction', default=0.001, show_default=True, type=float, help='Fraction of points to sample [0.0, 1.0].')
21
21
  @click.option('--block-size', nargs=3, type=int, default=(128, 128, 128), show_default=True, help='Block size in voxels (X Y Z).')
22
- def main(cv_path, mip, timestamp, output_dir, num_workers, cpus_per_worker, queue, fraction, block_size, sample_svids, worker_type):
22
+ @click.option('--bbox', nargs=6, type=int, default=None, help='Bounding box: begin_x begin_y begin_z end_x end_y end_z (in voxels).')
23
+ def main(cv_path, mip, timestamp, output_dir, num_workers, cpus_per_worker, queue, fraction, block_size, sample_svids, worker_type, bbox):
24
+
25
+ output_dir = os.path.abspath(output_dir)
23
26
 
24
27
  click.echo(f"Reading CloudVolume at {cv_path} (mip={mip}, timestamp={timestamp})")
25
28
 
@@ -48,6 +51,13 @@ def main(cv_path, mip, timestamp, output_dir, num_workers, cpus_per_worker, queu
48
51
  click.echo(f"Using SlurmWorker with queue '{queue}' and {cpus_per_worker} CPUs per worker.")
49
52
  worker_config = SlurmWorker(queue=queue, num_cpus=cpus_per_worker)
50
53
 
54
+ roi = None
55
+ if bbox is not None:
56
+ begin = bbox[:3]
57
+ end = bbox[3:]
58
+ shape = tuple(e - b for b, e in zip(begin, end))
59
+ roi = (begin, shape)
60
+
51
61
  task = SamplePoints(
52
62
  labels=labels,
53
63
  svids=svids,
@@ -55,7 +65,8 @@ def main(cv_path, mip, timestamp, output_dir, num_workers, cpus_per_worker, queu
55
65
  num_workers=num_workers,
56
66
  out_dir=points_dir,
57
67
  fraction=fraction,
58
- worker_config=worker_config
68
+ worker_config=worker_config,
69
+ roi=roi,
59
70
  )
60
71
 
61
72
  click.echo("Running task...")
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nps-cli
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: A simple CLI for sampling point clouds from large volumetric datasets
5
5
  Author: Jakob Troidl
6
6
  License-Expression: MIT
7
- Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.11
8
8
  Requires-Python: >=3.11
9
9
  Description-Content-Type: text/markdown
10
10
  Requires-Dist: click
@@ -17,7 +17,10 @@ Requires-Dist: build; extra == "dev"
17
17
  Requires-Dist: twine; extra == "dev"
18
18
  Requires-Dist: ruff; extra == "dev"
19
19
 
20
- # CLI for Point Cloud Sampling
20
+ [![pypi](https://badge.fury.io/py/nps-cli.svg)](https://badge.fury.io/py/nps-cli)
21
+ [![Python Version](https://img.shields.io/pypi/pyversions/nps-cli.svg)](https://pypi.org/project/nps-cli/)
22
+
23
+ # CLI for Distributed Point Cloud Sampling
21
24
 
22
25
  ```
23
26
  pip install nps-cli
@@ -34,7 +37,7 @@ Options:
34
37
  --timestamp INTEGER Optional timestamp for the dataset version
35
38
  (graphene only).
36
39
  --sample_svids Sample SVIDs in addition to points (default:
37
- False).
40
+ False) Graphene only.
38
41
  -o, --output-dir DIRECTORY Output directory. [default: ./nps_output]
39
42
  --worker-type [LocalWorker|LSFWorker|SlurmWorker]
40
43
  Type of worker to use for sampling.
@@ -46,6 +49,8 @@ Options:
46
49
  local]
47
50
  --fraction FLOAT Fraction of points to sample [0.0, 1.0].
48
51
  [default: 0.001]
52
+ --bbox INTEGER... Bounding box: begin_x begin_y begin_z
53
+ end_x end_y end_z (in voxels).
49
54
  --block-size INTEGER... Block size in voxels (X Y Z). [default:
50
55
  128, 128, 128]
51
56
  -h, --help Show this message and exit.
@@ -57,6 +62,12 @@ Options:
57
62
  nps --cv-path precomputed://gs://neuroglancer-janelia-flyem-hemibrain/v1.0/segmentation
58
63
  ```
59
64
 
65
+ Sample point clouds within a FlyEM Hemibrain subvolume:
66
+
67
+ ```bash
68
+ nps --cv-path precomputed://gs://neuroglancer-janelia-flyem-hemibrain/v1.0/segmentation --bbox 15347 19712 18606 15859 20224 19118 --fraction 0.01
69
+ ```
70
+
60
71
  ### Reading Point Clouds
61
72
 
62
73
  Please refer to the [pocaduck](https://github.com/JaneliaSciComp/pocaduck?tab=readme-ov-file#querying-point-clouds) repo on how to read point clouds from the output directory:
@@ -80,3 +91,10 @@ query.close()
80
91
  ```
81
92
 
82
93
  For optimized point cloud reading, consider [this](https://github.com/JaneliaSciComp/pocaduck?tab=readme-ov-file#running-the-optimization-pipeline).
94
+
95
+
96
+ ### Deploy
97
+ ```python
98
+ python -m build
99
+ twine upload dist/*
100
+ ```
@@ -1,8 +1,9 @@
1
1
  README.md
2
2
  pyproject.toml
3
3
  nps/__init__.py
4
- nps/config.py
5
4
  nps/main.py
5
+ nps/blockwise/__init__.py
6
+ nps/blockwise/sample_points.py
6
7
  nps_cli.egg-info/PKG-INFO
7
8
  nps_cli.egg-info/SOURCES.txt
8
9
  nps_cli.egg-info/dependency_links.txt
@@ -0,0 +1,2 @@
1
+ nps
2
+ nps_output
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nps-cli"
7
- version = "0.1.1"
7
+ version = "0.1.3"
8
8
  description = "A simple CLI for sampling point clouds from large volumetric datasets"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.11"
@@ -17,7 +17,7 @@ dependencies = [
17
17
  ]
18
18
 
19
19
  classifiers = [
20
- "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.11",
21
21
  ]
22
22
 
23
23
  [project.optional-dependencies]
@@ -36,7 +36,7 @@ sample-points = "nps.blockwise:SamplePoints"
36
36
  nps = "nps.main:main"
37
37
 
38
38
  [tool.setuptools.packages.find]
39
- include = ["nps"]
39
+ include = ["nps*"]
40
40
 
41
41
  [tool.ruff]
42
42
  exclude = ["dist", "build", "nps.egg-info", "volara_logs", "nps_output"]
@@ -1,5 +0,0 @@
1
- import yaml
2
-
3
- def load_config(path):
4
- with open(path, 'r') as f:
5
- return yaml.safe_load(f)
@@ -1 +0,0 @@
1
- nps
File without changes
File without changes