sdf-sampler 0.1.0__tar.gz → 0.3.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.
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/.gitignore +1 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/CHANGELOG.md +26 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/PKG-INFO +150 -39
- sdf_sampler-0.3.0/README.md +293 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/pyproject.toml +5 -2
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/__init__.py +1 -1
- sdf_sampler-0.3.0/src/sdf_sampler/__main__.py +17 -0
- sdf_sampler-0.3.0/src/sdf_sampler/cli.py +681 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampler.py +62 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/uv.lock +1 -1
- sdf_sampler-0.1.0/.github_token.env +0 -1
- sdf_sampler-0.1.0/README.md +0 -182
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/LICENSE +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/__init__.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/flood_fill.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/normal_idw.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/normal_offset.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/pocket.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/voxel_grid.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/algorithms/voxel_regions.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/analyzer.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/config.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/io.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/models/__init__.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/models/analysis.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/models/constraints.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/models/samples.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/__init__.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/box.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/brush.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/ray_carve.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/src/sdf_sampler/sampling/sphere.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/__init__.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_analyzer.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_equivalence.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_integration.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_models.py +0 -0
- {sdf_sampler-0.1.0 → sdf_sampler-0.3.0}/tests/test_sampler.py +0 -0
|
@@ -5,6 +5,32 @@ All notable changes to sdf-sampler will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.0] - 2025-01-29
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Full parameter control** in CLI and SDK
|
|
13
|
+
- All analysis options exposed: `--min-gap-size`, `--cone-angle`, `--idw-sample-count`, etc.
|
|
14
|
+
- All sampling options exposed: `--samples-per-primitive`, `--inverse-square-falloff`, etc.
|
|
15
|
+
- Output mode control: `--flood-fill-output`, `--voxel-regions-output` (boxes/samples/both)
|
|
16
|
+
- **Surface point inclusion**
|
|
17
|
+
- `--include-surface-points` flag to include original points with phi=0
|
|
18
|
+
- `--surface-point-ratio` to control fraction included (default 10%)
|
|
19
|
+
- SDK: `sampler.generate(..., include_surface_points=True, surface_point_ratio=0.1)`
|
|
20
|
+
|
|
21
|
+
## [0.2.0] - 2025-01-29
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Command-Line Interface** for batch processing
|
|
26
|
+
- `sdf-sampler pipeline` - Full workflow (analyze + sample + export)
|
|
27
|
+
- `sdf-sampler analyze` - Detect SOLID/EMPTY regions
|
|
28
|
+
- `sdf-sampler sample` - Generate training samples from constraints
|
|
29
|
+
- `sdf-sampler info` - Inspect point clouds, constraints, and sample files
|
|
30
|
+
- Support for `python -m sdf_sampler` invocation
|
|
31
|
+
- Console script entry point (`sdf-sampler` command)
|
|
32
|
+
- Comprehensive README with SDK and CLI documentation
|
|
33
|
+
|
|
8
34
|
## [0.1.0] - 2025-01-29
|
|
9
35
|
|
|
10
36
|
### Added
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sdf-sampler
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Auto-analysis and sampling of point clouds for SDF (Signed Distance Field) training data generation
|
|
5
|
-
Project-URL: Repository, https://github.com/
|
|
5
|
+
Project-URL: Repository, https://github.com/Chiark-Collective/sdf-sampler
|
|
6
6
|
Author-email: Liam <liam@example.com>
|
|
7
7
|
License: MIT
|
|
8
8
|
License-File: LICENSE
|
|
@@ -60,7 +60,88 @@ For additional I/O format support (PLY, LAS/LAZ):
|
|
|
60
60
|
pip install sdf-sampler[io]
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
##
|
|
63
|
+
## Command-Line Interface
|
|
64
|
+
|
|
65
|
+
sdf-sampler provides a CLI for common workflows:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Run as module
|
|
69
|
+
python -m sdf_sampler --help
|
|
70
|
+
|
|
71
|
+
# Or use the installed command
|
|
72
|
+
sdf-sampler --help
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Commands
|
|
76
|
+
|
|
77
|
+
#### `pipeline` - Full workflow (recommended)
|
|
78
|
+
|
|
79
|
+
Run the complete pipeline: analyze point cloud → generate samples → export.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Basic usage
|
|
83
|
+
sdf-sampler pipeline scan.ply -o training_data.parquet
|
|
84
|
+
|
|
85
|
+
# With options
|
|
86
|
+
sdf-sampler pipeline scan.ply \
|
|
87
|
+
-o training_data.parquet \
|
|
88
|
+
-n 50000 \
|
|
89
|
+
-s inverse_square \
|
|
90
|
+
--save-constraints constraints.json \
|
|
91
|
+
-v
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Options:
|
|
95
|
+
- `-o, --output`: Output parquet file (default: `<input>_samples.parquet`)
|
|
96
|
+
- `-n, --total-samples`: Number of samples to generate (default: 10000)
|
|
97
|
+
- `-s, --strategy`: Sampling strategy: `constant`, `density`, `inverse_square` (default: `inverse_square`)
|
|
98
|
+
- `-a, --algorithms`: Specific algorithms to run (default: all)
|
|
99
|
+
- `--save-constraints`: Also save constraints to JSON
|
|
100
|
+
- `--seed`: Random seed for reproducibility
|
|
101
|
+
- `-v, --verbose`: Verbose output
|
|
102
|
+
|
|
103
|
+
#### `analyze` - Detect regions
|
|
104
|
+
|
|
105
|
+
Analyze a point cloud to detect SOLID/EMPTY regions.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
sdf-sampler analyze scan.ply -o constraints.json -v
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Options:
|
|
112
|
+
- `-o, --output`: Output JSON file (default: `<input>_constraints.json`)
|
|
113
|
+
- `-a, --algorithms`: Algorithms to run (see below)
|
|
114
|
+
- `--no-hull-filter`: Disable hull filtering
|
|
115
|
+
- `-v, --verbose`: Verbose output
|
|
116
|
+
|
|
117
|
+
#### `sample` - Generate training samples
|
|
118
|
+
|
|
119
|
+
Generate training samples from a constraints file.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
sdf-sampler sample scan.ply constraints.json -o samples.parquet -n 50000
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Options:
|
|
126
|
+
- `-o, --output`: Output parquet file
|
|
127
|
+
- `-n, --total-samples`: Number of samples (default: 10000)
|
|
128
|
+
- `-s, --strategy`: Sampling strategy (default: `inverse_square`)
|
|
129
|
+
- `--seed`: Random seed
|
|
130
|
+
- `-v, --verbose`: Verbose output
|
|
131
|
+
|
|
132
|
+
#### `info` - Inspect files
|
|
133
|
+
|
|
134
|
+
Show information about point clouds, constraints, or sample files.
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
sdf-sampler info scan.ply
|
|
138
|
+
sdf-sampler info constraints.json
|
|
139
|
+
sdf-sampler info samples.parquet
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Python SDK
|
|
143
|
+
|
|
144
|
+
### Quick Start
|
|
64
145
|
|
|
65
146
|
```python
|
|
66
147
|
from sdf_sampler import SDFAnalyzer, SDFSampler, load_point_cloud
|
|
@@ -86,28 +167,13 @@ samples = sampler.generate(
|
|
|
86
167
|
sampler.export_parquet(samples, "training_data.parquet")
|
|
87
168
|
```
|
|
88
169
|
|
|
89
|
-
## Features
|
|
90
|
-
|
|
91
|
-
### Auto-Analysis Algorithms
|
|
92
|
-
|
|
93
|
-
- **flood_fill**: Detects EMPTY (outside) regions by ray propagation from sky
|
|
94
|
-
- **voxel_regions**: Detects SOLID (underground) regions
|
|
95
|
-
- **normal_offset**: Generates paired SOLID/EMPTY boxes along surface normals
|
|
96
|
-
- **normal_idw**: Inverse distance weighted sampling along normals
|
|
97
|
-
- **pocket**: Detects interior cavities
|
|
98
|
-
|
|
99
|
-
### Sampling Strategies
|
|
100
|
-
|
|
101
|
-
- **CONSTANT**: Fixed number of samples per constraint
|
|
102
|
-
- **DENSITY**: Samples proportional to constraint volume
|
|
103
|
-
- **INVERSE_SQUARE**: More samples near surface, fewer far away (recommended)
|
|
104
|
-
|
|
105
|
-
## API Reference
|
|
106
|
-
|
|
107
170
|
### SDFAnalyzer
|
|
108
171
|
|
|
172
|
+
Analyzes point clouds to detect SOLID and EMPTY regions.
|
|
173
|
+
|
|
109
174
|
```python
|
|
110
|
-
from sdf_sampler import SDFAnalyzer
|
|
175
|
+
from sdf_sampler import SDFAnalyzer
|
|
176
|
+
from sdf_sampler.config import AnalyzerConfig, AutoAnalysisOptions
|
|
111
177
|
|
|
112
178
|
# With default config
|
|
113
179
|
analyzer = SDFAnalyzer()
|
|
@@ -136,10 +202,23 @@ print(f"EMPTY: {result.summary.empty_constraints}")
|
|
|
136
202
|
constraints = result.constraints
|
|
137
203
|
```
|
|
138
204
|
|
|
205
|
+
#### Analysis Algorithms
|
|
206
|
+
|
|
207
|
+
| Algorithm | Description | Output |
|
|
208
|
+
|-----------|-------------|--------|
|
|
209
|
+
| `flood_fill` | Detects EMPTY (outside) regions by ray propagation from sky | Box or SamplePoint constraints |
|
|
210
|
+
| `voxel_regions` | Detects SOLID (underground) regions | Box or SamplePoint constraints |
|
|
211
|
+
| `normal_offset` | Generates paired SOLID/EMPTY boxes along surface normals | Box constraints |
|
|
212
|
+
| `normal_idw` | Inverse distance weighted sampling along normals | SamplePoint constraints |
|
|
213
|
+
| `pocket` | Detects interior cavities | Pocket constraints |
|
|
214
|
+
|
|
139
215
|
### SDFSampler
|
|
140
216
|
|
|
217
|
+
Generates training samples from constraints.
|
|
218
|
+
|
|
141
219
|
```python
|
|
142
|
-
from sdf_sampler import SDFSampler
|
|
220
|
+
from sdf_sampler import SDFSampler
|
|
221
|
+
from sdf_sampler.config import SamplerConfig
|
|
143
222
|
|
|
144
223
|
# With default config
|
|
145
224
|
sampler = SDFSampler()
|
|
@@ -167,6 +246,14 @@ sampler.export_parquet(samples, "output.parquet")
|
|
|
167
246
|
df = sampler.to_dataframe(samples)
|
|
168
247
|
```
|
|
169
248
|
|
|
249
|
+
#### Sampling Strategies
|
|
250
|
+
|
|
251
|
+
| Strategy | Description |
|
|
252
|
+
|----------|-------------|
|
|
253
|
+
| `constant` | Fixed number of samples per constraint |
|
|
254
|
+
| `density` | Samples proportional to constraint volume |
|
|
255
|
+
| `inverse_square` | More samples near surface, fewer far away (recommended) |
|
|
256
|
+
|
|
170
257
|
### Constraint Types
|
|
171
258
|
|
|
172
259
|
The analyzer generates various constraint types:
|
|
@@ -180,6 +267,22 @@ Each constraint has:
|
|
|
180
267
|
- `sign`: "solid" (negative SDF) or "empty" (positive SDF)
|
|
181
268
|
- `weight`: Sample weight (default 1.0)
|
|
182
269
|
|
|
270
|
+
### I/O Helpers
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
from sdf_sampler import load_point_cloud, export_parquet
|
|
274
|
+
|
|
275
|
+
# Load various formats
|
|
276
|
+
xyz, normals = load_point_cloud("scan.ply") # PLY (requires trimesh)
|
|
277
|
+
xyz, normals = load_point_cloud("scan.las") # LAS/LAZ (requires laspy)
|
|
278
|
+
xyz, normals = load_point_cloud("scan.csv") # CSV with x,y,z columns
|
|
279
|
+
xyz, normals = load_point_cloud("scan.npz") # NumPy archive
|
|
280
|
+
xyz, normals = load_point_cloud("scan.parquet") # Parquet
|
|
281
|
+
|
|
282
|
+
# Export samples
|
|
283
|
+
export_parquet(samples, "output.parquet")
|
|
284
|
+
```
|
|
285
|
+
|
|
183
286
|
## Output Format
|
|
184
287
|
|
|
185
288
|
The exported parquet file contains columns:
|
|
@@ -194,32 +297,40 @@ The exported parquet file contains columns:
|
|
|
194
297
|
| is_surface | bool | Whether sample is on surface |
|
|
195
298
|
| is_free | bool | Whether sample is in free space (EMPTY) |
|
|
196
299
|
|
|
197
|
-
## Configuration
|
|
300
|
+
## Configuration Reference
|
|
198
301
|
|
|
199
302
|
### AnalyzerConfig
|
|
200
303
|
|
|
201
304
|
| Option | Default | Description |
|
|
202
305
|
|--------|---------|-------------|
|
|
203
|
-
| min_gap_size | 0.10 | Minimum gap size for flood fill (meters) |
|
|
204
|
-
| max_grid_dim | 200 | Maximum voxel grid dimension |
|
|
205
|
-
| cone_angle | 15.0 | Ray propagation cone half-angle (degrees) |
|
|
206
|
-
| normal_offset_pairs | 40 | Number of box pairs for normal_offset |
|
|
207
|
-
| idw_sample_count | 1000 | Total IDW samples |
|
|
208
|
-
| idw_max_distance | 0.5 | Maximum IDW distance (meters) |
|
|
209
|
-
| hull_filter_enabled | True | Filter outside X-Y alpha shape |
|
|
210
|
-
| hull_alpha | 1.0 | Alpha shape parameter |
|
|
306
|
+
| `min_gap_size` | 0.10 | Minimum gap size for flood fill (meters) |
|
|
307
|
+
| `max_grid_dim` | 200 | Maximum voxel grid dimension |
|
|
308
|
+
| `cone_angle` | 15.0 | Ray propagation cone half-angle (degrees) |
|
|
309
|
+
| `normal_offset_pairs` | 40 | Number of box pairs for normal_offset |
|
|
310
|
+
| `idw_sample_count` | 1000 | Total IDW samples |
|
|
311
|
+
| `idw_max_distance` | 0.5 | Maximum IDW distance (meters) |
|
|
312
|
+
| `hull_filter_enabled` | True | Filter outside X-Y alpha shape |
|
|
313
|
+
| `hull_alpha` | 1.0 | Alpha shape parameter |
|
|
211
314
|
|
|
212
315
|
### SamplerConfig
|
|
213
316
|
|
|
214
317
|
| Option | Default | Description |
|
|
215
318
|
|--------|---------|-------------|
|
|
216
|
-
| total_samples | 10000 | Default total samples |
|
|
217
|
-
| samples_per_primitive | 100 | Samples per constraint (CONSTANT) |
|
|
218
|
-
| samples_per_cubic_meter | 10000 | Sample density (DENSITY) |
|
|
219
|
-
| inverse_square_base_samples | 100 | Base samples (INVERSE_SQUARE) |
|
|
220
|
-
| inverse_square_falloff | 2.0 | Falloff exponent |
|
|
221
|
-
| near_band | 0.02 | Near-band width |
|
|
222
|
-
| seed | 0 | Random seed |
|
|
319
|
+
| `total_samples` | 10000 | Default total samples |
|
|
320
|
+
| `samples_per_primitive` | 100 | Samples per constraint (CONSTANT) |
|
|
321
|
+
| `samples_per_cubic_meter` | 10000 | Sample density (DENSITY) |
|
|
322
|
+
| `inverse_square_base_samples` | 100 | Base samples (INVERSE_SQUARE) |
|
|
323
|
+
| `inverse_square_falloff` | 2.0 | Falloff exponent |
|
|
324
|
+
| `near_band` | 0.02 | Near-band width |
|
|
325
|
+
| `seed` | 0 | Random seed |
|
|
326
|
+
|
|
327
|
+
## Integration with Ubik
|
|
328
|
+
|
|
329
|
+
sdf-sampler is the core analysis engine for [Ubik](https://github.com/Chiark-Collective/ubik), an interactive web application for SDF labeling. Use sdf-sampler directly for:
|
|
330
|
+
|
|
331
|
+
- Automated batch processing pipelines
|
|
332
|
+
- Integration into ML training workflows
|
|
333
|
+
- Custom analysis scripts
|
|
223
334
|
|
|
224
335
|
## License
|
|
225
336
|
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# sdf-sampler
|
|
2
|
+
|
|
3
|
+
Auto-analysis and sampling of point clouds for SDF (Signed Distance Field) training data generation.
|
|
4
|
+
|
|
5
|
+
A lightweight, standalone Python package for generating SDF training hints from point clouds. Automatically detects SOLID (inside) and EMPTY (outside) regions and generates training samples suitable for SDF regression models.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install sdf-sampler
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For additional I/O format support (PLY, LAS/LAZ):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install sdf-sampler[io]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Command-Line Interface
|
|
20
|
+
|
|
21
|
+
sdf-sampler provides a CLI for common workflows:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Run as module
|
|
25
|
+
python -m sdf_sampler --help
|
|
26
|
+
|
|
27
|
+
# Or use the installed command
|
|
28
|
+
sdf-sampler --help
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Commands
|
|
32
|
+
|
|
33
|
+
#### `pipeline` - Full workflow (recommended)
|
|
34
|
+
|
|
35
|
+
Run the complete pipeline: analyze point cloud → generate samples → export.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Basic usage
|
|
39
|
+
sdf-sampler pipeline scan.ply -o training_data.parquet
|
|
40
|
+
|
|
41
|
+
# With options
|
|
42
|
+
sdf-sampler pipeline scan.ply \
|
|
43
|
+
-o training_data.parquet \
|
|
44
|
+
-n 50000 \
|
|
45
|
+
-s inverse_square \
|
|
46
|
+
--save-constraints constraints.json \
|
|
47
|
+
-v
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Options:
|
|
51
|
+
- `-o, --output`: Output parquet file (default: `<input>_samples.parquet`)
|
|
52
|
+
- `-n, --total-samples`: Number of samples to generate (default: 10000)
|
|
53
|
+
- `-s, --strategy`: Sampling strategy: `constant`, `density`, `inverse_square` (default: `inverse_square`)
|
|
54
|
+
- `-a, --algorithms`: Specific algorithms to run (default: all)
|
|
55
|
+
- `--save-constraints`: Also save constraints to JSON
|
|
56
|
+
- `--seed`: Random seed for reproducibility
|
|
57
|
+
- `-v, --verbose`: Verbose output
|
|
58
|
+
|
|
59
|
+
#### `analyze` - Detect regions
|
|
60
|
+
|
|
61
|
+
Analyze a point cloud to detect SOLID/EMPTY regions.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
sdf-sampler analyze scan.ply -o constraints.json -v
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Options:
|
|
68
|
+
- `-o, --output`: Output JSON file (default: `<input>_constraints.json`)
|
|
69
|
+
- `-a, --algorithms`: Algorithms to run (see below)
|
|
70
|
+
- `--no-hull-filter`: Disable hull filtering
|
|
71
|
+
- `-v, --verbose`: Verbose output
|
|
72
|
+
|
|
73
|
+
#### `sample` - Generate training samples
|
|
74
|
+
|
|
75
|
+
Generate training samples from a constraints file.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
sdf-sampler sample scan.ply constraints.json -o samples.parquet -n 50000
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Options:
|
|
82
|
+
- `-o, --output`: Output parquet file
|
|
83
|
+
- `-n, --total-samples`: Number of samples (default: 10000)
|
|
84
|
+
- `-s, --strategy`: Sampling strategy (default: `inverse_square`)
|
|
85
|
+
- `--seed`: Random seed
|
|
86
|
+
- `-v, --verbose`: Verbose output
|
|
87
|
+
|
|
88
|
+
#### `info` - Inspect files
|
|
89
|
+
|
|
90
|
+
Show information about point clouds, constraints, or sample files.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
sdf-sampler info scan.ply
|
|
94
|
+
sdf-sampler info constraints.json
|
|
95
|
+
sdf-sampler info samples.parquet
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Python SDK
|
|
99
|
+
|
|
100
|
+
### Quick Start
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from sdf_sampler import SDFAnalyzer, SDFSampler, load_point_cloud
|
|
104
|
+
|
|
105
|
+
# 1. Load point cloud (supports PLY, LAS, CSV, NPZ, Parquet)
|
|
106
|
+
xyz, normals = load_point_cloud("scan.ply")
|
|
107
|
+
|
|
108
|
+
# 2. Auto-analyze to detect EMPTY/SOLID regions
|
|
109
|
+
analyzer = SDFAnalyzer()
|
|
110
|
+
result = analyzer.analyze(xyz=xyz, normals=normals)
|
|
111
|
+
print(f"Generated {len(result.constraints)} constraints")
|
|
112
|
+
|
|
113
|
+
# 3. Generate training samples
|
|
114
|
+
sampler = SDFSampler()
|
|
115
|
+
samples = sampler.generate(
|
|
116
|
+
xyz=xyz,
|
|
117
|
+
constraints=result.constraints,
|
|
118
|
+
strategy="inverse_square",
|
|
119
|
+
total_samples=50000,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# 4. Export to parquet
|
|
123
|
+
sampler.export_parquet(samples, "training_data.parquet")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### SDFAnalyzer
|
|
127
|
+
|
|
128
|
+
Analyzes point clouds to detect SOLID and EMPTY regions.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from sdf_sampler import SDFAnalyzer
|
|
132
|
+
from sdf_sampler.config import AnalyzerConfig, AutoAnalysisOptions
|
|
133
|
+
|
|
134
|
+
# With default config
|
|
135
|
+
analyzer = SDFAnalyzer()
|
|
136
|
+
|
|
137
|
+
# With custom config
|
|
138
|
+
analyzer = SDFAnalyzer(config=AnalyzerConfig(
|
|
139
|
+
min_gap_size=0.10, # Minimum gap for flood fill
|
|
140
|
+
max_grid_dim=200, # Maximum voxel grid dimension
|
|
141
|
+
cone_angle=15.0, # Ray propagation cone angle
|
|
142
|
+
hull_filter_enabled=True, # Filter outside X-Y hull
|
|
143
|
+
))
|
|
144
|
+
|
|
145
|
+
# Run analysis
|
|
146
|
+
result = analyzer.analyze(
|
|
147
|
+
xyz=xyz, # (N, 3) point positions
|
|
148
|
+
normals=normals, # (N, 3) point normals (optional)
|
|
149
|
+
algorithms=["flood_fill", "voxel_regions"], # Which algorithms to run
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Access results
|
|
153
|
+
print(f"Total constraints: {result.summary.total_constraints}")
|
|
154
|
+
print(f"SOLID: {result.summary.solid_constraints}")
|
|
155
|
+
print(f"EMPTY: {result.summary.empty_constraints}")
|
|
156
|
+
|
|
157
|
+
# Get constraint dicts for sampling
|
|
158
|
+
constraints = result.constraints
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### Analysis Algorithms
|
|
162
|
+
|
|
163
|
+
| Algorithm | Description | Output |
|
|
164
|
+
|-----------|-------------|--------|
|
|
165
|
+
| `flood_fill` | Detects EMPTY (outside) regions by ray propagation from sky | Box or SamplePoint constraints |
|
|
166
|
+
| `voxel_regions` | Detects SOLID (underground) regions | Box or SamplePoint constraints |
|
|
167
|
+
| `normal_offset` | Generates paired SOLID/EMPTY boxes along surface normals | Box constraints |
|
|
168
|
+
| `normal_idw` | Inverse distance weighted sampling along normals | SamplePoint constraints |
|
|
169
|
+
| `pocket` | Detects interior cavities | Pocket constraints |
|
|
170
|
+
|
|
171
|
+
### SDFSampler
|
|
172
|
+
|
|
173
|
+
Generates training samples from constraints.
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from sdf_sampler import SDFSampler
|
|
177
|
+
from sdf_sampler.config import SamplerConfig
|
|
178
|
+
|
|
179
|
+
# With default config
|
|
180
|
+
sampler = SDFSampler()
|
|
181
|
+
|
|
182
|
+
# With custom config
|
|
183
|
+
sampler = SDFSampler(config=SamplerConfig(
|
|
184
|
+
total_samples=10000,
|
|
185
|
+
inverse_square_base_samples=100,
|
|
186
|
+
inverse_square_falloff=2.0,
|
|
187
|
+
near_band=0.02,
|
|
188
|
+
))
|
|
189
|
+
|
|
190
|
+
# Generate samples
|
|
191
|
+
samples = sampler.generate(
|
|
192
|
+
xyz=xyz, # Point cloud for distance computation
|
|
193
|
+
constraints=constraints, # From analyzer.analyze().constraints
|
|
194
|
+
strategy="inverse_square", # Sampling strategy
|
|
195
|
+
seed=42, # For reproducibility
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Export
|
|
199
|
+
sampler.export_parquet(samples, "output.parquet")
|
|
200
|
+
|
|
201
|
+
# Or get DataFrame
|
|
202
|
+
df = sampler.to_dataframe(samples)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### Sampling Strategies
|
|
206
|
+
|
|
207
|
+
| Strategy | Description |
|
|
208
|
+
|----------|-------------|
|
|
209
|
+
| `constant` | Fixed number of samples per constraint |
|
|
210
|
+
| `density` | Samples proportional to constraint volume |
|
|
211
|
+
| `inverse_square` | More samples near surface, fewer far away (recommended) |
|
|
212
|
+
|
|
213
|
+
### Constraint Types
|
|
214
|
+
|
|
215
|
+
The analyzer generates various constraint types:
|
|
216
|
+
|
|
217
|
+
- **BoxConstraint**: Axis-aligned bounding box
|
|
218
|
+
- **SphereConstraint**: Spherical region
|
|
219
|
+
- **SamplePointConstraint**: Direct point with signed distance
|
|
220
|
+
- **PocketConstraint**: Detected cavity region
|
|
221
|
+
|
|
222
|
+
Each constraint has:
|
|
223
|
+
- `sign`: "solid" (negative SDF) or "empty" (positive SDF)
|
|
224
|
+
- `weight`: Sample weight (default 1.0)
|
|
225
|
+
|
|
226
|
+
### I/O Helpers
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from sdf_sampler import load_point_cloud, export_parquet
|
|
230
|
+
|
|
231
|
+
# Load various formats
|
|
232
|
+
xyz, normals = load_point_cloud("scan.ply") # PLY (requires trimesh)
|
|
233
|
+
xyz, normals = load_point_cloud("scan.las") # LAS/LAZ (requires laspy)
|
|
234
|
+
xyz, normals = load_point_cloud("scan.csv") # CSV with x,y,z columns
|
|
235
|
+
xyz, normals = load_point_cloud("scan.npz") # NumPy archive
|
|
236
|
+
xyz, normals = load_point_cloud("scan.parquet") # Parquet
|
|
237
|
+
|
|
238
|
+
# Export samples
|
|
239
|
+
export_parquet(samples, "output.parquet")
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Output Format
|
|
243
|
+
|
|
244
|
+
The exported parquet file contains columns:
|
|
245
|
+
|
|
246
|
+
| Column | Type | Description |
|
|
247
|
+
|--------|------|-------------|
|
|
248
|
+
| x, y, z | float | 3D position |
|
|
249
|
+
| phi | float | Signed distance (negative=solid, positive=empty) |
|
|
250
|
+
| nx, ny, nz | float | Normal vector (if available) |
|
|
251
|
+
| weight | float | Sample weight |
|
|
252
|
+
| source | string | Sample origin (e.g., "box_solid", "flood_fill_empty") |
|
|
253
|
+
| is_surface | bool | Whether sample is on surface |
|
|
254
|
+
| is_free | bool | Whether sample is in free space (EMPTY) |
|
|
255
|
+
|
|
256
|
+
## Configuration Reference
|
|
257
|
+
|
|
258
|
+
### AnalyzerConfig
|
|
259
|
+
|
|
260
|
+
| Option | Default | Description |
|
|
261
|
+
|--------|---------|-------------|
|
|
262
|
+
| `min_gap_size` | 0.10 | Minimum gap size for flood fill (meters) |
|
|
263
|
+
| `max_grid_dim` | 200 | Maximum voxel grid dimension |
|
|
264
|
+
| `cone_angle` | 15.0 | Ray propagation cone half-angle (degrees) |
|
|
265
|
+
| `normal_offset_pairs` | 40 | Number of box pairs for normal_offset |
|
|
266
|
+
| `idw_sample_count` | 1000 | Total IDW samples |
|
|
267
|
+
| `idw_max_distance` | 0.5 | Maximum IDW distance (meters) |
|
|
268
|
+
| `hull_filter_enabled` | True | Filter outside X-Y alpha shape |
|
|
269
|
+
| `hull_alpha` | 1.0 | Alpha shape parameter |
|
|
270
|
+
|
|
271
|
+
### SamplerConfig
|
|
272
|
+
|
|
273
|
+
| Option | Default | Description |
|
|
274
|
+
|--------|---------|-------------|
|
|
275
|
+
| `total_samples` | 10000 | Default total samples |
|
|
276
|
+
| `samples_per_primitive` | 100 | Samples per constraint (CONSTANT) |
|
|
277
|
+
| `samples_per_cubic_meter` | 10000 | Sample density (DENSITY) |
|
|
278
|
+
| `inverse_square_base_samples` | 100 | Base samples (INVERSE_SQUARE) |
|
|
279
|
+
| `inverse_square_falloff` | 2.0 | Falloff exponent |
|
|
280
|
+
| `near_band` | 0.02 | Near-band width |
|
|
281
|
+
| `seed` | 0 | Random seed |
|
|
282
|
+
|
|
283
|
+
## Integration with Ubik
|
|
284
|
+
|
|
285
|
+
sdf-sampler is the core analysis engine for [Ubik](https://github.com/Chiark-Collective/ubik), an interactive web application for SDF labeling. Use sdf-sampler directly for:
|
|
286
|
+
|
|
287
|
+
- Automated batch processing pipelines
|
|
288
|
+
- Integration into ML training workflows
|
|
289
|
+
- Custom analysis scripts
|
|
290
|
+
|
|
291
|
+
## License
|
|
292
|
+
|
|
293
|
+
MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sdf-sampler"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Auto-analysis and sampling of point clouds for SDF (Signed Distance Field) training data generation"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -42,8 +42,11 @@ dev = [
|
|
|
42
42
|
]
|
|
43
43
|
all = ["sdf-sampler[io,dev]"]
|
|
44
44
|
|
|
45
|
+
[project.scripts]
|
|
46
|
+
sdf-sampler = "sdf_sampler.cli:main"
|
|
47
|
+
|
|
45
48
|
[project.urls]
|
|
46
|
-
Repository = "https://github.com/
|
|
49
|
+
Repository = "https://github.com/Chiark-Collective/sdf-sampler"
|
|
47
50
|
|
|
48
51
|
[build-system]
|
|
49
52
|
requires = ["hatchling"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# ABOUTME: Entry point for running sdf-sampler as a module
|
|
2
|
+
# ABOUTME: Enables `python -m sdf_sampler` invocation
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Run sdf-sampler as a module.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python -m sdf_sampler --help
|
|
9
|
+
python -m sdf_sampler analyze input.ply -o constraints.json
|
|
10
|
+
python -m sdf_sampler sample input.ply constraints.json -o samples.parquet
|
|
11
|
+
python -m sdf_sampler pipeline input.ply -o samples.parquet
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from sdf_sampler.cli import main
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
raise SystemExit(main())
|