voxeldata 0.0.1__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.
@@ -0,0 +1,27 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025-2026, The VoxelData developers
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are
6
+ permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this list of
9
+ conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
12
+ of conditions and the following disclaimer in the documentation and/or other
13
+ materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its contributors may be
16
+ used to endorse or promote products derived from this software without specific prior
17
+ written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY
20
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22
+ THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: voxeldata
3
+ Version: 0.0.1
4
+ Summary: Spatially referenced voxel arrays built on xarray.
5
+ Author: Samuel Le Meur-Diebolt
6
+ License-Expression: BSD-3-Clause
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Topic :: Scientific/Engineering
17
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
18
+ Classifier: Typing :: Typed
19
+ Requires-Dist: numpy>=2.3.4
20
+ Requires-Dist: pandas>=2.2.0
21
+ Requires-Dist: xarray>=2025.1.0
22
+ Requires-Python: >=3.11, <3.15
23
+ Description-Content-Type: text/markdown
24
+
25
+ # VoxelData
26
+
27
+ **VoxelData** is a small, modality-independent data model for spatially referenced
28
+ voxel arrays, built on [xarray](https://docs.xarray.dev).
29
+
30
+ A VoxelData array is an `xarray.DataArray` that is always:
31
+
32
+ - dimensioned `(*nonspatial_dims, k, j, i)` — leading dimensions may appear in any
33
+ order, including optional special dimensions `time` and `grid`; the native voxel
34
+ indices `k`/`j`/`i` are always present and always last;
35
+ - backed by a single [`VoxelToWorldIndex`](src/voxeldata/geometry.py) attached to
36
+ the world coordinates `z`/`y`/`x`, which derives them lazily from one affine
37
+ shared by all data or one affine per `grid`.
38
+
39
+ World coordinates are never stored directly. This keeps cropping, slicing,
40
+ registration, and I/O consistent: the affine always describes the whole physical
41
+ grid, and indexing updates it exactly.
42
+
43
+ ## Installation
44
+
45
+ ```bash
46
+ uv add voxeldata
47
+ ```
48
+
49
+ Or with pip:
50
+
51
+ ```bash
52
+ pip install voxeldata
53
+ ```
54
+
55
+ ## Quick start
56
+
57
+ ```python
58
+ import numpy as np
59
+ import voxeldata as vd
60
+
61
+ data = vd.create_voxeldata(
62
+ np.zeros((3, 64, 128)),
63
+ dims=("k", "j", "i"),
64
+ spacing=(0.5, 0.1, 0.1), # z, y, x in millimetres
65
+ origin=(0.0, 0.0, 0.0),
66
+ )
67
+
68
+ data.coords["x"] # lazily derived world coordinates
69
+ data.sel(x=5.0) # select in world space
70
+ vd.validate_voxeldata(data)
71
+
72
+ data.vd.origin # {'z': 0.0, 'y': 0.0, 'x': 0.0}
73
+ data.vd.spacing # {'k': 0.5, 'j': 0.1, 'i': 0.1}
74
+ data.vd.direction # (3, 3) world direction matrix
75
+ ```
76
+
77
+ Oblique grids work the same way:
78
+
79
+ ```python
80
+ data = vd.create_voxeldata(
81
+ np.zeros((3, 64, 128)),
82
+ dims=("k", "j", "i"),
83
+ voxel_to_world=affine, # (4, 4) homogeneous affine
84
+ )
85
+ ```
86
+
87
+ The `grid` dimension selects a spatial sampling grid: each grid has one affine,
88
+ shared across time and all other nonspatial dimensions. Affines may be identical
89
+ across grids. A `grid` dimension requires an `(ngrid, 4, 4)` affine stack, even for
90
+ one grid; without it, geometry uses a single `(4, 4)` affine.
91
+
92
+ Grid-dependent geometry carries one affine per grid:
93
+
94
+ ```python
95
+ data = vd.create_voxeldata(
96
+ np.zeros((2, 3, 64, 128)),
97
+ dims=("grid", "k", "j", "i"),
98
+ grid=np.array([0, 1]),
99
+ voxel_to_world=affines, # (2, 4, 4)
100
+ )
101
+ ```
102
+
103
+ Creation preserves the supplied nonspatial dimension order. For example, both
104
+ `(time, grid, k, j, i)` and `(grid, time, k, j, i)` are valid.
105
+ `data.vd.canonicalize()` and `data.vd.ensure()` normalize to
106
+ `(...extra_dims, grid, time, k, j, i)`, preserving the relative order of extra
107
+ dimensions and omitting absent `grid`/`time` dimensions. Canonicalization also
108
+ restores scalar-selected spatial dimensions, but does not restore a selected grid.
109
+
110
+ ## Public API
111
+
112
+ | Area | Functions |
113
+ |---|---|
114
+ | Dimensions | `VOXEL_DIMS`, `WORLD_DIMS`, `GRID_DIM`, `TIME_DIM`, `CORE_DIMS` |
115
+ | Construction | `create_voxeldata`, `attach_voxel_to_world_index` |
116
+ | Accessor | `data.vd` ([`VoxelDataAccessor`](src/voxeldata/accessors.py)) |
117
+ | Geometry | `get_voxel_to_world_affine`, `get_voxel_to_world_units`, `get_voxel_to_world_index_origin`, `get_voxel_to_world_index_spacing`, `get_voxel_to_world_direction_matrix`, `convert_length_units` |
118
+ | Validation | `validate_voxeldata`, `ensure_voxeldata`, `canonicalize_voxeldata` |
119
+ | Affine operations | `apply_affine`, `reindex_voxels`, `reindex_voxels_like`, `get_bounding_box`, `get_relative_affine` |
120
+ | Named affines | `data.vd.get_affine`, `set_affine`, `remove_affine`, `apply`, `to` |
121
+
122
+ The `VoxelToWorldIndex` and `VoxelToWorldTransform` classes implement the xarray
123
+ indexing machinery and are exported for advanced use.
124
+
125
+ ## Relationship to ConfUSIus
126
+
127
+ VoxelData is extracted from [ConfUSIus](https://github.com/confusius-tools/confusius),
128
+ which uses it for functional ultrasound imaging data. The model itself makes no
129
+ assumption about modality, acquisition hardware, or signal semantics, so it is
130
+ usable by any package working with spatially referenced voxel data.
131
+
132
+ ## Development
133
+
134
+ ```bash
135
+ uv sync # install dependencies
136
+ just test # run tests
137
+ just pre-commit # run lint, format, type checks, docstring checks
138
+ just docs # build documentation
139
+ ```
140
+
141
+ ## License
142
+
143
+ BSD-3-Clause. Portions derived from ConfUSIus; see `NOTICE`.
@@ -0,0 +1,119 @@
1
+ # VoxelData
2
+
3
+ **VoxelData** is a small, modality-independent data model for spatially referenced
4
+ voxel arrays, built on [xarray](https://docs.xarray.dev).
5
+
6
+ A VoxelData array is an `xarray.DataArray` that is always:
7
+
8
+ - dimensioned `(*nonspatial_dims, k, j, i)` — leading dimensions may appear in any
9
+ order, including optional special dimensions `time` and `grid`; the native voxel
10
+ indices `k`/`j`/`i` are always present and always last;
11
+ - backed by a single [`VoxelToWorldIndex`](src/voxeldata/geometry.py) attached to
12
+ the world coordinates `z`/`y`/`x`, which derives them lazily from one affine
13
+ shared by all data or one affine per `grid`.
14
+
15
+ World coordinates are never stored directly. This keeps cropping, slicing,
16
+ registration, and I/O consistent: the affine always describes the whole physical
17
+ grid, and indexing updates it exactly.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ uv add voxeldata
23
+ ```
24
+
25
+ Or with pip:
26
+
27
+ ```bash
28
+ pip install voxeldata
29
+ ```
30
+
31
+ ## Quick start
32
+
33
+ ```python
34
+ import numpy as np
35
+ import voxeldata as vd
36
+
37
+ data = vd.create_voxeldata(
38
+ np.zeros((3, 64, 128)),
39
+ dims=("k", "j", "i"),
40
+ spacing=(0.5, 0.1, 0.1), # z, y, x in millimetres
41
+ origin=(0.0, 0.0, 0.0),
42
+ )
43
+
44
+ data.coords["x"] # lazily derived world coordinates
45
+ data.sel(x=5.0) # select in world space
46
+ vd.validate_voxeldata(data)
47
+
48
+ data.vd.origin # {'z': 0.0, 'y': 0.0, 'x': 0.0}
49
+ data.vd.spacing # {'k': 0.5, 'j': 0.1, 'i': 0.1}
50
+ data.vd.direction # (3, 3) world direction matrix
51
+ ```
52
+
53
+ Oblique grids work the same way:
54
+
55
+ ```python
56
+ data = vd.create_voxeldata(
57
+ np.zeros((3, 64, 128)),
58
+ dims=("k", "j", "i"),
59
+ voxel_to_world=affine, # (4, 4) homogeneous affine
60
+ )
61
+ ```
62
+
63
+ The `grid` dimension selects a spatial sampling grid: each grid has one affine,
64
+ shared across time and all other nonspatial dimensions. Affines may be identical
65
+ across grids. A `grid` dimension requires an `(ngrid, 4, 4)` affine stack, even for
66
+ one grid; without it, geometry uses a single `(4, 4)` affine.
67
+
68
+ Grid-dependent geometry carries one affine per grid:
69
+
70
+ ```python
71
+ data = vd.create_voxeldata(
72
+ np.zeros((2, 3, 64, 128)),
73
+ dims=("grid", "k", "j", "i"),
74
+ grid=np.array([0, 1]),
75
+ voxel_to_world=affines, # (2, 4, 4)
76
+ )
77
+ ```
78
+
79
+ Creation preserves the supplied nonspatial dimension order. For example, both
80
+ `(time, grid, k, j, i)` and `(grid, time, k, j, i)` are valid.
81
+ `data.vd.canonicalize()` and `data.vd.ensure()` normalize to
82
+ `(...extra_dims, grid, time, k, j, i)`, preserving the relative order of extra
83
+ dimensions and omitting absent `grid`/`time` dimensions. Canonicalization also
84
+ restores scalar-selected spatial dimensions, but does not restore a selected grid.
85
+
86
+ ## Public API
87
+
88
+ | Area | Functions |
89
+ |---|---|
90
+ | Dimensions | `VOXEL_DIMS`, `WORLD_DIMS`, `GRID_DIM`, `TIME_DIM`, `CORE_DIMS` |
91
+ | Construction | `create_voxeldata`, `attach_voxel_to_world_index` |
92
+ | Accessor | `data.vd` ([`VoxelDataAccessor`](src/voxeldata/accessors.py)) |
93
+ | Geometry | `get_voxel_to_world_affine`, `get_voxel_to_world_units`, `get_voxel_to_world_index_origin`, `get_voxel_to_world_index_spacing`, `get_voxel_to_world_direction_matrix`, `convert_length_units` |
94
+ | Validation | `validate_voxeldata`, `ensure_voxeldata`, `canonicalize_voxeldata` |
95
+ | Affine operations | `apply_affine`, `reindex_voxels`, `reindex_voxels_like`, `get_bounding_box`, `get_relative_affine` |
96
+ | Named affines | `data.vd.get_affine`, `set_affine`, `remove_affine`, `apply`, `to` |
97
+
98
+ The `VoxelToWorldIndex` and `VoxelToWorldTransform` classes implement the xarray
99
+ indexing machinery and are exported for advanced use.
100
+
101
+ ## Relationship to ConfUSIus
102
+
103
+ VoxelData is extracted from [ConfUSIus](https://github.com/confusius-tools/confusius),
104
+ which uses it for functional ultrasound imaging data. The model itself makes no
105
+ assumption about modality, acquisition hardware, or signal semantics, so it is
106
+ usable by any package working with spatially referenced voxel data.
107
+
108
+ ## Development
109
+
110
+ ```bash
111
+ uv sync # install dependencies
112
+ just test # run tests
113
+ just pre-commit # run lint, format, type checks, docstring checks
114
+ just docs # build documentation
115
+ ```
116
+
117
+ ## License
118
+
119
+ BSD-3-Clause. Portions derived from ConfUSIus; see `NOTICE`.
@@ -0,0 +1,88 @@
1
+ [project]
2
+ name = "voxeldata"
3
+ version = "0.0.1"
4
+ description = "Spatially referenced voxel arrays built on xarray."
5
+ readme = "README.md"
6
+ license = "BSD-3-Clause"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.11,<3.15"
9
+ classifiers = [
10
+ "Development Status :: 3 - Alpha",
11
+ "Programming Language :: Python",
12
+ "Programming Language :: Python :: 3.11",
13
+ "Programming Language :: Python :: 3.12",
14
+ "Programming Language :: Python :: 3.13",
15
+ "Programming Language :: Python :: 3.14",
16
+ "Operating System :: OS Independent",
17
+ "Intended Audience :: Science/Research",
18
+ "Topic :: Scientific/Engineering",
19
+ "Topic :: Scientific/Engineering :: Image Processing",
20
+ "Typing :: Typed",
21
+ ]
22
+ dependencies = [
23
+ "numpy>=2.3.4",
24
+ "pandas>=2.2.0",
25
+ "xarray>=2025.1.0",
26
+ ]
27
+
28
+ [[project.authors]]
29
+ name = "Samuel Le Meur-Diebolt"
30
+
31
+ [build-system]
32
+ requires = ["uv-build>=0.9.5,<0.14.0"]
33
+ build-backend = "uv_build"
34
+
35
+ [dependency-groups]
36
+ dev = [
37
+ "codespell>=2.3.0",
38
+ "numpydoc>=1.11.0",
39
+ "prek>=0.4.8",
40
+ "pytest>=9.1.1",
41
+ "pytest-cov>=6.0.0",
42
+ "ruff>=0.16.5",
43
+ "ty>=0.0.77,<0.1.0",
44
+ "zensical>=0.0.67",
45
+ "mkdocstrings[python]>=1.0.4",
46
+ ]
47
+
48
+ [tool.numpydoc_validation]
49
+ exclude = [
50
+ '\.__init__$',
51
+ '\._.*$',
52
+ ]
53
+ checks = [
54
+ "all",
55
+ "GL01",
56
+ "ES01",
57
+ "SA01",
58
+ "EX01",
59
+ ]
60
+
61
+ [tool.ruff]
62
+ line-length = 88
63
+
64
+ [tool.ty.src]
65
+ include = [
66
+ "src",
67
+ "tests",
68
+ ]
69
+
70
+ [tool.pytest.ini_options]
71
+ minversion = "8.0"
72
+ testpaths = ["tests"]
73
+ pythonpath = ["."]
74
+ python_files = ["test_*.py"]
75
+ python_classes = ["Test*"]
76
+ python_functions = ["test_*"]
77
+ addopts = [
78
+ "-ra",
79
+ "--strict-markers",
80
+ "--strict-config",
81
+ "--import-mode=importlib",
82
+ "--showlocals",
83
+ "--cov=voxeldata",
84
+ "--cov-report=term-missing",
85
+ "--cov-report=html",
86
+ "--cov-report=xml",
87
+ ]
88
+ filterwarnings = ["error"]
@@ -0,0 +1,83 @@
1
+ [project]
2
+ name = "voxeldata"
3
+ version = "0.0.1"
4
+ description = "Spatially referenced voxel arrays built on xarray."
5
+ readme = "README.md"
6
+ license = "BSD-3-Clause"
7
+ license-files = ["LICENSE"]
8
+ authors = [{ name = "Samuel Le Meur-Diebolt" }]
9
+ requires-python = ">=3.11,<3.15"
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Programming Language :: Python",
13
+ "Programming Language :: Python :: 3.11",
14
+ "Programming Language :: Python :: 3.12",
15
+ "Programming Language :: Python :: 3.13",
16
+ "Programming Language :: Python :: 3.14",
17
+ "Operating System :: OS Independent",
18
+ "Intended Audience :: Science/Research",
19
+ "Topic :: Scientific/Engineering",
20
+ "Topic :: Scientific/Engineering :: Image Processing",
21
+ "Typing :: Typed",
22
+ ]
23
+ dependencies = [
24
+ "numpy>=2.3.4",
25
+ "pandas>=2.2.0",
26
+ "xarray>=2025.1.0",
27
+ ]
28
+
29
+ [build-system]
30
+ requires = ["uv-build>=0.9.5,<0.14.0"]
31
+ build-backend = "uv_build"
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ "codespell>=2.3.0",
36
+ "numpydoc>=1.11.0",
37
+ "prek>=0.4.8",
38
+ "pytest>=9.1.1",
39
+ "pytest-cov>=6.0.0",
40
+ "ruff>=0.16.5",
41
+ "ty>=0.0.77,<0.1.0",
42
+ "zensical>=0.0.67",
43
+ "mkdocstrings[python]>=1.0.4",
44
+ ]
45
+
46
+ [tool.numpydoc_validation]
47
+ exclude = [
48
+ '\.__init__$',
49
+ '\._.*$',
50
+ ]
51
+ checks = [
52
+ "all",
53
+ "GL01",
54
+ "ES01",
55
+ "SA01",
56
+ "EX01",
57
+ ]
58
+
59
+ [tool.ruff]
60
+ line-length = 88
61
+
62
+ [tool.ty.src]
63
+ include = ["src", "tests"]
64
+
65
+ [tool.pytest.ini_options]
66
+ minversion = "8.0"
67
+ testpaths = ["tests"]
68
+ pythonpath = ["."]
69
+ python_files = ["test_*.py"]
70
+ python_classes = ["Test*"]
71
+ python_functions = ["test_*"]
72
+ addopts = [
73
+ "-ra",
74
+ "--strict-markers",
75
+ "--strict-config",
76
+ "--import-mode=importlib",
77
+ "--showlocals",
78
+ "--cov=voxeldata",
79
+ "--cov-report=term-missing",
80
+ "--cov-report=html",
81
+ "--cov-report=xml",
82
+ ]
83
+ filterwarnings = ["error"]
@@ -0,0 +1,82 @@
1
+ """VoxelData: spatially referenced voxel arrays built on xarray.
2
+
3
+ VoxelData defines a small, modality-independent data model for arrays gridded in
4
+ space. A VoxelData array is an `xarray.DataArray` with native voxel dimensions
5
+ `(k, j, i)` (plus any extra leading dimensions) and a single
6
+ [VoxelToWorldIndex][voxeldata.geometry.VoxelToWorldIndex] that lazily derives world
7
+ coordinates `(z, y, x)` from a voxel-to-world affine.
8
+ """
9
+
10
+ from voxeldata._dims import CORE_DIMS, GRID_DIM, TIME_DIM, VOXEL_DIMS, WORLD_DIMS
11
+ from voxeldata.accessors import VoxelDataAccessor
12
+ from voxeldata.affine import (
13
+ apply_affine,
14
+ get_bounding_box,
15
+ get_relative_affine,
16
+ reindex_voxels,
17
+ reindex_voxels_like,
18
+ )
19
+ from voxeldata.create import create_voxeldata
20
+ from voxeldata.geometry import (
21
+ VoxelToWorldIndex,
22
+ VoxelToWorldTransform,
23
+ attach_voxel_to_world_index,
24
+ convert_length_units,
25
+ get_affine_axis_scalings,
26
+ get_affine_direction_matrix,
27
+ get_voxel_to_world_affine,
28
+ get_voxel_to_world_direction_matrix,
29
+ get_voxel_to_world_index,
30
+ get_voxel_to_world_index_origin,
31
+ get_voxel_to_world_index_spacing,
32
+ get_voxel_to_world_spatial_dims,
33
+ get_voxel_to_world_units,
34
+ has_axis_aligned_voxel_to_world_index,
35
+ has_voxel_to_world_index,
36
+ require_scalar_grid_affine,
37
+ restore_voxel_to_world_index,
38
+ )
39
+ from voxeldata.validation import (
40
+ canonicalize_voxeldata,
41
+ ensure_voxeldata,
42
+ require_dataarray,
43
+ require_positive_finite,
44
+ validate_voxeldata,
45
+ )
46
+
47
+ __all__ = [
48
+ "CORE_DIMS",
49
+ "GRID_DIM",
50
+ "TIME_DIM",
51
+ "VOXEL_DIMS",
52
+ "WORLD_DIMS",
53
+ "VoxelDataAccessor",
54
+ "VoxelToWorldIndex",
55
+ "VoxelToWorldTransform",
56
+ "apply_affine",
57
+ "attach_voxel_to_world_index",
58
+ "canonicalize_voxeldata",
59
+ "convert_length_units",
60
+ "create_voxeldata",
61
+ "ensure_voxeldata",
62
+ "get_affine_axis_scalings",
63
+ "get_affine_direction_matrix",
64
+ "get_bounding_box",
65
+ "get_relative_affine",
66
+ "get_voxel_to_world_affine",
67
+ "get_voxel_to_world_direction_matrix",
68
+ "get_voxel_to_world_index",
69
+ "get_voxel_to_world_index_origin",
70
+ "get_voxel_to_world_index_spacing",
71
+ "get_voxel_to_world_spatial_dims",
72
+ "get_voxel_to_world_units",
73
+ "has_axis_aligned_voxel_to_world_index",
74
+ "has_voxel_to_world_index",
75
+ "reindex_voxels",
76
+ "reindex_voxels_like",
77
+ "require_dataarray",
78
+ "require_positive_finite",
79
+ "require_scalar_grid_affine",
80
+ "restore_voxel_to_world_index",
81
+ "validate_voxeldata",
82
+ ]