nps-cli 0.1.2__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.
- {nps_cli-0.1.2 → nps_cli-0.1.3}/PKG-INFO +1 -1
- nps_cli-0.1.3/nps/blockwise/__init__.py +1 -0
- nps_cli-0.1.3/nps/blockwise/sample_points.py +132 -0
- {nps_cli-0.1.2 → nps_cli-0.1.3}/nps/main.py +2 -0
- {nps_cli-0.1.2 → nps_cli-0.1.3}/nps_cli.egg-info/PKG-INFO +1 -1
- {nps_cli-0.1.2 → nps_cli-0.1.3}/nps_cli.egg-info/SOURCES.txt +2 -0
- nps_cli-0.1.3/nps_cli.egg-info/top_level.txt +2 -0
- {nps_cli-0.1.2 → nps_cli-0.1.3}/pyproject.toml +2 -2
- nps_cli-0.1.2/nps_cli.egg-info/top_level.txt +0 -1
- {nps_cli-0.1.2 → nps_cli-0.1.3}/README.md +0 -0
- {nps_cli-0.1.2 → nps_cli-0.1.3}/nps/__init__.py +0 -0
- {nps_cli-0.1.2 → nps_cli-0.1.3}/nps_cli.egg-info/dependency_links.txt +0 -0
- {nps_cli-0.1.2 → nps_cli-0.1.3}/nps_cli.egg-info/entry_points.txt +0 -0
- {nps_cli-0.1.2 → nps_cli-0.1.3}/nps_cli.egg-info/requires.txt +0 -0
- {nps_cli-0.1.2 → nps_cli-0.1.3}/setup.cfg +0 -0
|
@@ -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
|
|
@@ -22,6 +22,8 @@ from volara.workers import LSFWorker, LocalWorker, SlurmWorker
|
|
|
22
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
23
|
def main(cv_path, mip, timestamp, output_dir, num_workers, cpus_per_worker, queue, fraction, block_size, sample_svids, worker_type, bbox):
|
|
24
24
|
|
|
25
|
+
output_dir = os.path.abspath(output_dir)
|
|
26
|
+
|
|
25
27
|
click.echo(f"Reading CloudVolume at {cv_path} (mip={mip}, timestamp={timestamp})")
|
|
26
28
|
|
|
27
29
|
labels_name = os.path.dirname(output_dir) + "_labels"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nps-cli"
|
|
7
|
-
version = "0.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"
|
|
@@ -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 +0,0 @@
|
|
|
1
|
-
nps
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|