simind-python-connector 1.0.1__tar.gz → 1.1.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.
- {simind_python_connector-1.0.1/simind_python_connector.egg-info → simind_python_connector-1.1.1}/PKG-INFO +1 -1
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/backends.rst +1 -1
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/changelog.rst +126 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/conf.py +1 -1
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1/docs}/examples/07A_stir_adaptor_osem.py +4 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1/docs}/examples/07B_sirf_adaptor_osem.py +4 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/07C_pytomography_adaptor_osem.py +11 -15
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1/docs}/examples/_python_connector_helpers.py +3 -2
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/geometry.rst +29 -6
- {simind_python_connector-1.0.1/docs → simind_python_connector-1.1.1}/examples/07A_stir_adaptor_osem.py +4 -0
- {simind_python_connector-1.0.1/docs → simind_python_connector-1.1.1}/examples/07B_sirf_adaptor_osem.py +4 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/07C_pytomography_adaptor_osem.py +11 -15
- {simind_python_connector-1.0.1/docs → simind_python_connector-1.1.1}/examples/_python_connector_helpers.py +3 -2
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/pyproject.toml +2 -2
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/__init__.py +1 -1
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/backends/__init__.py +7 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/backends/sirf_backend.py +3 -3
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/builders/acquisition_builder.py +110 -35
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/builders/image_builder.py +21 -6
- simind_python_connector-1.1.1/simind_python_connector/connectors/__init__.py +43 -0
- simind_python_connector-1.1.1/simind_python_connector/connectors/_spacing.py +83 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/connectors/python_connector.py +165 -40
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/connectors/pytomography_adaptor.py +22 -24
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/connectors/sirf_adaptor.py +15 -2
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/connectors/stir_adaptor.py +15 -2
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/converters/attenuation.py +15 -15
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/converters/simind_to_stir.py +37 -26
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/core/config.py +249 -66
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/core/executor.py +48 -8
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/utils/import_helpers.py +4 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/utils/interfile_numpy.py +18 -8
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/utils/interfile_parser.py +15 -4
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/utils/simind_utils.py +50 -11
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/utils/stir_utils.py +11 -6
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1/simind_python_connector.egg-info}/PKG-INFO +1 -1
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector.egg-info/SOURCES.txt +7 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_acquisition_builder_unit.py +108 -4
- simind_python_connector-1.1.1/tests/test_attenuation_utils.py +126 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_backends.py +109 -0
- simind_python_connector-1.1.1/tests/test_connector_backend_separation.py +118 -0
- simind_python_connector-1.1.1/tests/test_container_scripts.py +290 -0
- simind_python_connector-1.1.1/tests/test_dicom_builder.py +169 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_example_config_profile.py +26 -0
- simind_python_connector-1.1.1/tests/test_executor.py +246 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_image_builder_unit.py +53 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_interfile_numpy.py +128 -4
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_interfile_parser.py +19 -0
- simind_python_connector-1.1.1/tests/test_legacy_surface_policy.py +20 -0
- simind_python_connector-1.1.1/tests/test_marker_policy.py +117 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_native_adaptors.py +197 -69
- simind_python_connector-1.1.1/tests/test_non_square_slices_simind.py +64 -0
- simind_python_connector-1.1.1/tests/test_packaging_resources.py +113 -0
- simind_python_connector-1.1.1/tests/test_python_connector.py +748 -0
- simind_python_connector-1.1.1/tests/test_pytomography_adaptor.py +390 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_schneider_density.py +8 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_simind_to_stir_converter.py +82 -9
- simind_python_connector-1.1.1/tests/test_simulation_config.py +221 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_stir_utils_backend_choice_unit.py +1 -1
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_utils.py +3 -8
- simind_python_connector-1.1.1/tests/test_utils_small.py +96 -0
- simind_python_connector-1.0.1/simind_python_connector/connectors/__init__.py +0 -24
- simind_python_connector-1.0.1/simind_python_connector/connectors/_spacing.py +0 -69
- simind_python_connector-1.0.1/tests/test_connector_backend_separation.py +0 -42
- simind_python_connector-1.0.1/tests/test_marker_policy.py +0 -72
- simind_python_connector-1.0.1/tests/test_python_connector.py +0 -370
- simind_python_connector-1.0.1/tests/test_pytomography_adaptor.py +0 -190
- simind_python_connector-1.0.1/tests/test_simulation_config.py +0 -89
- simind_python_connector-1.0.1/tests/test_utils_small.py +0 -53
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/LICENSE +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/MANIFEST.in +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/README.md +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/api.rst +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/connector_modernization_plan.rst +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/contributing.rst +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/01_basic_simulation.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/02_runtime_switch_comparison.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/03_multi_window.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/04_custom_config.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/05_scattwin_vs_penetrate_comparison.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/06_schneider_density_conversion.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/08A_stir_adaptor_from_dicom.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/08B_sirf_adaptor_from_dicom.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples/08C_pytomography_adaptor_from_dicom.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/examples.rst +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/index.rst +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/installation.rst +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/intro.rst +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/testing.rst +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/docs/usage.rst +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/01_basic_simulation.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/02_runtime_switch_comparison.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/03_multi_window.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/04_custom_config.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/05_scattwin_vs_penetrate_comparison.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/06_schneider_density_conversion.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/08A_stir_adaptor_from_dicom.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/08B_sirf_adaptor_from_dicom.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/examples/08C_pytomography_adaptor_from_dicom.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/requirements-dev.txt +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/requirements.txt +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/setup.cfg +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/backends/base.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/backends/stir_backend.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/builders/__init__.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/configs/AnyScan.yaml +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/configs/Discovery670.yaml +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/configs/Example.yaml +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/configs/MLD001_SCAN0.yaml +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/configs/__init__.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/configs/input.smc +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/connectors/base.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/converters/__init__.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/converters/dicom_to_stir.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/core/__init__.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/core/types.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/data/Schneider2000.json +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/data/__init__.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/data/bone.atn +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/data/h2o.atn +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/utils/__init__.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/utils/backend_access.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/utils/io_utils.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector/utils/sirf_stir_utils.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector.egg-info/dependency_links.txt +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector.egg-info/requires.txt +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/simind_python_connector.egg-info/top_level.txt +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/__init__.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/conftest.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_arithmetic_operations.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_connectors_base.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_container_library_isolation.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_examples_backend_separation.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_geometry_isolation_forward_projection.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_integration.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_osem_geometry_diagnostics.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_package_identity.py +0 -0
- {simind_python_connector-1.0.1 → simind_python_connector-1.1.1}/tests/test_sirf_stir_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simind-python-connector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Python SIMIND Monte Carlo connector with STIR/SIRF/PyTomography adaptors
|
|
5
5
|
Author-email: Sam Porter <sam.porter.18@ucl.ac.uk>, Rebecca Gillen <rebecca.gillen.18@ucl.ac.uk>, "Efstathios (Stahis) Varzakis" <efstathios.varzakis.22@ucl.ac.uk>
|
|
6
6
|
Maintainer-email: Sam Porter <sam.porter.18@ucl.ac.uk>
|
|
@@ -16,7 +16,7 @@ Dependency Matrix
|
|
|
16
16
|
* - ``SimindPythonConnector``
|
|
17
17
|
- SIMIND executable on ``PATH`` (no SIRF/STIR/PyTomography requirement)
|
|
18
18
|
* - ``StirSimindAdaptor``
|
|
19
|
-
- STIR Python (``stir``)
|
|
19
|
+
- STIR Python (``stir`` and ``stirextra``)
|
|
20
20
|
* - ``SirfSimindAdaptor``
|
|
21
21
|
- SIRF (``sirf.STIR``)
|
|
22
22
|
* - ``PyTomographySimindAdaptor``
|
|
@@ -7,6 +7,132 @@ All notable changes to this project are documented in this file.
|
|
|
7
7
|
|
|
8
8
|
The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_, and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.
|
|
9
9
|
|
|
10
|
+
1.1.1 - 2026-10-11
|
|
11
|
+
------------------
|
|
12
|
+
|
|
13
|
+
Fixed
|
|
14
|
+
~~~~~
|
|
15
|
+
|
|
16
|
+
- A negative Index 1 (energies from the isotope file) turned the mu map into
|
|
17
|
+
near-vacuum; it is now converted at ``abs(Index 1)``.
|
|
18
|
+
- Acquisition arrays use ``(tof, axial, view, bin)``, and DICOM matrix sizes and
|
|
19
|
+
pixel spacing are no longer swapped, so non-square projections build
|
|
20
|
+
correctly. ``create_stir_acqdata`` takes ``(bins, axial)`` and uses the same
|
|
21
|
+
layout.
|
|
22
|
+
- The PyTomography adaptor reverses axial slices to match PyTomography's SIMIND
|
|
23
|
+
reader; reconstructions were flipped along z.
|
|
24
|
+
- The STIR/SIRF adaptors raise a ``ValueError`` for non-cubic voxels instead of
|
|
25
|
+
using the z spacing for every axis.
|
|
26
|
+
- The adaptors default ``scoring_routine`` to None, which keeps the
|
|
27
|
+
configuration's Index 84; they used to overwrite it with SCATTWIN.
|
|
28
|
+
- The geometry docs give the projection axis orders correctly.
|
|
29
|
+
- PyTomography PENETRATE and fallback outputs use the reader's
|
|
30
|
+
``(view, bin, axial)`` order, and PyTomography scatter orders are validated
|
|
31
|
+
like the core's.
|
|
32
|
+
- The STIR/SIRF adaptors reject a mu map whose voxel size differs from the
|
|
33
|
+
source's.
|
|
34
|
+
- The legacy SIRF attenuation loader reverses the voxel sizes with the
|
|
35
|
+
dimensions.
|
|
36
|
+
- The SIRF wrapper's ``maximum()`` and ``sapyb()`` update the wrapped object.
|
|
37
|
+
- Piecewise Schneider conversion gives HU outside the table the nearest
|
|
38
|
+
segment's density, so metal above 4001 HU no longer becomes air.
|
|
39
|
+
- MPI runs use ``$SIMIND_MPI_BIN``, else ``simind_mpi`` next to the serial
|
|
40
|
+
binary, with ``/MP`` and without a stray ``-p``.
|
|
41
|
+
- An output header that cannot be read raises instead of being skipped.
|
|
42
|
+
- YAML export keeps unnamed parameters and flags.
|
|
43
|
+
- New header keys are written before ``!END OF INTERFILE``, and ``set()``
|
|
44
|
+
changes the last duplicate key, the one readers use.
|
|
45
|
+
- A header converted into another directory points at its data by absolute
|
|
46
|
+
path, and an orbit file named like an ignored key keeps its radii.
|
|
47
|
+
- DICOM frames are split by ``EnergyWindowVector`` when present; each output
|
|
48
|
+
declares one window and its share of the duration.
|
|
49
|
+
- ``run()`` sets Flags 5 and 14, which SIMIND needs to write readable headers,
|
|
50
|
+
so config-defined phantom runs work.
|
|
51
|
+
- Example 07C no longer drops attenuation and PSF modelling silently, and the
|
|
52
|
+
examples accept ``SIMIND_BIN``.
|
|
53
|
+
- Importing the converter no longer configures root logging.
|
|
54
|
+
- setuptools 77.0.3 or newer is required to build.
|
|
55
|
+
|
|
56
|
+
Changed
|
|
57
|
+
~~~~~~~
|
|
58
|
+
|
|
59
|
+
- ``get_attenuation_coefficient`` takes keV, as documented.
|
|
60
|
+
- ``NN`` must be an integer >= 1, switches reject None and bool values,
|
|
61
|
+
``quantization_scale`` must be in (0, 1], and ``output_prefix`` must match
|
|
62
|
+
``[a-z0-9][a-z0-9_-]*`` (SIMIND lowercases file names).
|
|
63
|
+
- Publishing waits for the tests and checks the tag against the version; tests
|
|
64
|
+
also run on ``release/**`` branches.
|
|
65
|
+
|
|
66
|
+
1.1.0 - 2026-10-11
|
|
67
|
+
------------------
|
|
68
|
+
|
|
69
|
+
Replaces 2.0.0 and 3.0.0, which were published by mistake and have been
|
|
70
|
+
withdrawn.
|
|
71
|
+
|
|
72
|
+
Breaking Changes
|
|
73
|
+
~~~~~~~~~~~~~~~~
|
|
74
|
+
|
|
75
|
+
- The projection bin size and matrix (Index 28, 76, 77) come from the
|
|
76
|
+
configuration. ``configure_voxel_phantom`` no longer sets them from the
|
|
77
|
+
phantom image, which made the projections square whenever the image slices
|
|
78
|
+
were square and took the number of axial rows from the image height.
|
|
79
|
+
- ``configure_voxel_phantom`` keeps the configured scoring routine (Index 84)
|
|
80
|
+
unless ``scoring_routine`` is given.
|
|
81
|
+
- ``STIRSPECTAcquisitionDataBuilder`` no longer flips projection data along the
|
|
82
|
+
last axis.
|
|
83
|
+
- Runtime-operator switches passed to ``run()`` apply to that run only. They
|
|
84
|
+
are no longer kept on the connector for later runs.
|
|
85
|
+
- Each run deletes earlier outputs that share the output prefix before SIMIND
|
|
86
|
+
starts.
|
|
87
|
+
- The STIR backend needs ``stirextra`` as well as ``stir``.
|
|
88
|
+
- STIR/SIRF adaptors take the z voxel spacing from a different axis (see
|
|
89
|
+
Fixed). Results change for anisotropic voxels.
|
|
90
|
+
- Invalid inputs now raise ``ValueError``: empty, non-finite or negative source
|
|
91
|
+
and attenuation maps, array shapes that do not match the header, and
|
|
92
|
+
``output_prefix`` values that contain a path. Energy-window validation raises
|
|
93
|
+
``ValueError`` instead of ``AssertionError``.
|
|
94
|
+
|
|
95
|
+
Added
|
|
96
|
+
~~~~~
|
|
97
|
+
|
|
98
|
+
- ``run()`` warns when a voxel phantom is wider or longer than the projection
|
|
99
|
+
field of view.
|
|
100
|
+
|
|
101
|
+
Fixed
|
|
102
|
+
~~~~~
|
|
103
|
+
|
|
104
|
+
- The packaged ``configs/input.smc`` is now included in the wheel. SMC parsing
|
|
105
|
+
handles fixed-width fields and checks section counts.
|
|
106
|
+
- STIR/SIRF adaptors read the z voxel spacing from the correct axis:
|
|
107
|
+
``voxel_sizes()`` is ``(z, y, x)``, and raw ``get_grid_spacing()`` is
|
|
108
|
+
``(unused, z, y, x)``.
|
|
109
|
+
- Voxel-map dimension indices 78, 79, 81 and 82 are set correctly.
|
|
110
|
+
- The executor resolves ``SIMIND_BIN`` and accepts executable paths that
|
|
111
|
+
contain spaces. The connector no longer changes the process working directory.
|
|
112
|
+
- Runtime switches apply to one run only. Output prefixes cannot escape the
|
|
113
|
+
output directory. Stale outputs are removed, and ``.win``, ``.smi`` and
|
|
114
|
+
``.dmi`` inputs are kept.
|
|
115
|
+
- Energy-window validation raises ``ValueError`` instead of failing an
|
|
116
|
+
``assert``.
|
|
117
|
+
- The Interfile loader honours data offsets, strips quoted values and rejects
|
|
118
|
+
truncated or oversized payloads. The SIMIND-to-STIR converter keeps data-file
|
|
119
|
+
lines and applies the radius scale factor.
|
|
120
|
+
- Attenuation utilities handle the ``rho*1000`` selector and STIR ``#`` header
|
|
121
|
+
keys, and reject unknown attenuation types.
|
|
122
|
+
- Image and acquisition builders validate array shapes, split multi-energy data
|
|
123
|
+
deterministically and no longer flip images. The DICOM builder handles missing
|
|
124
|
+
timing information and 2D pixel data.
|
|
125
|
+
- Backend adaptors are imported lazily, so the core package imports without
|
|
126
|
+
STIR, SIRF or PyTomography.
|
|
127
|
+
|
|
128
|
+
Removed
|
|
129
|
+
~~~~~~~
|
|
130
|
+
|
|
131
|
+
- The legacy ``scripts/simulation.py``.
|
|
132
|
+
- The "non-square matrix" warning from
|
|
133
|
+
``SimulationConfig.validate_parameters``; SIMIND supports non-square
|
|
134
|
+
projections.
|
|
135
|
+
|
|
10
136
|
1.0.1 - 2026-04-16
|
|
11
137
|
------------------
|
|
12
138
|
|
|
@@ -13,7 +13,7 @@ sys.path.insert(0, os.path.abspath(".."))
|
|
|
13
13
|
project = "simind-python-connector"
|
|
14
14
|
copyright = f"{datetime.now().year}, Sam Porter, Efstathios Varzakis"
|
|
15
15
|
author = "Sam Porter, Efstathios Varzakis"
|
|
16
|
-
release = "1.
|
|
16
|
+
release = "1.1.1"
|
|
17
17
|
for dist_name in ("simind-python-connector",):
|
|
18
18
|
try:
|
|
19
19
|
release = importlib_metadata.version(dist_name)
|
|
@@ -188,6 +188,10 @@ def main() -> None:
|
|
|
188
188
|
adaptor.add_config_value(1, 140.0)
|
|
189
189
|
adaptor.add_config_value(19, 2)
|
|
190
190
|
adaptor.add_config_value(53, 0)
|
|
191
|
+
# Projections on the reconstruction grid: 32 x 32 bins of 4 mm (Index 28 in cm).
|
|
192
|
+
adaptor.add_config_value(28, 0.4)
|
|
193
|
+
adaptor.add_config_value(76, 32)
|
|
194
|
+
adaptor.add_config_value(77, 32)
|
|
191
195
|
adaptor.run()
|
|
192
196
|
|
|
193
197
|
projection = adaptor.get_total_output(window=1)
|
|
@@ -170,6 +170,10 @@ def main() -> None:
|
|
|
170
170
|
adaptor.add_config_value(1, 140.0)
|
|
171
171
|
adaptor.add_config_value(19, 2)
|
|
172
172
|
adaptor.add_config_value(53, 0)
|
|
173
|
+
# Projections on the reconstruction grid: 32 x 32 bins of 4 mm (Index 28 in cm).
|
|
174
|
+
adaptor.add_config_value(28, 0.4)
|
|
175
|
+
adaptor.add_config_value(76, 32)
|
|
176
|
+
adaptor.add_config_value(77, 32)
|
|
173
177
|
adaptor.run()
|
|
174
178
|
|
|
175
179
|
projection = adaptor.get_total_output(window=1)
|
|
@@ -90,21 +90,13 @@ def _build_system_matrix(
|
|
|
90
90
|
) -> SPECTSystemMatrix:
|
|
91
91
|
object_meta, proj_meta = pytomo_simind.get_metadata(str(projection_header))
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
except Exception:
|
|
101
|
-
pass
|
|
102
|
-
|
|
103
|
-
try:
|
|
104
|
-
psf_meta = pytomo_simind.get_psfmeta_from_header(str(projection_header))
|
|
105
|
-
obj2obj_transforms.append(SPECTPSFTransform(psf_meta=psf_meta))
|
|
106
|
-
except Exception:
|
|
107
|
-
pass
|
|
93
|
+
psf_meta = pytomo_simind.get_psfmeta_from_header(str(projection_header))
|
|
94
|
+
obj2obj_transforms = [
|
|
95
|
+
SPECTAttenuationTransform(
|
|
96
|
+
attenuation_map=attenuation_map_xyz.to(dtype=torch.float32).contiguous()
|
|
97
|
+
),
|
|
98
|
+
SPECTPSFTransform(psf_meta=psf_meta),
|
|
99
|
+
]
|
|
108
100
|
|
|
109
101
|
return SPECTSystemMatrix(
|
|
110
102
|
obj2obj_transforms=obj2obj_transforms,
|
|
@@ -144,6 +136,10 @@ def main() -> None:
|
|
|
144
136
|
adaptor.add_config_value(1, 140.0)
|
|
145
137
|
adaptor.add_config_value(19, 2)
|
|
146
138
|
adaptor.add_config_value(53, 0)
|
|
139
|
+
# Projections on the reconstruction grid: 32 x 32 bins of 4 mm (Index 28 in cm).
|
|
140
|
+
adaptor.add_config_value(28, 0.4)
|
|
141
|
+
adaptor.add_config_value(76, 32)
|
|
142
|
+
adaptor.add_config_value(77, 32)
|
|
147
143
|
adaptor.run()
|
|
148
144
|
|
|
149
145
|
projection_tensor = adaptor.get_total_output(window=1).to(dtype=torch.float32)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
import shutil
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
@@ -8,9 +9,9 @@ from simind_python_connector import SimindPythonConnector
|
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def require_simind() -> None:
|
|
11
|
-
if shutil.which("simind") is None:
|
|
12
|
+
if shutil.which(os.environ.get("SIMIND_BIN") or "simind") is None:
|
|
12
13
|
raise RuntimeError(
|
|
13
|
-
"SIMIND executable not found
|
|
14
|
+
"SIMIND executable not found ($SIMIND_BIN or simind on PATH). "
|
|
14
15
|
"Install SIMIND before running this example."
|
|
15
16
|
)
|
|
16
17
|
|
|
@@ -9,9 +9,9 @@ At-a-Glance Axis Conventions
|
|
|
9
9
|
|
|
10
10
|
- STIR/SIRF image arrays are handled as ``(z, y, x)``.
|
|
11
11
|
- STIR/SIRF projection arrays are often 4D with a singleton TOF axis:
|
|
12
|
-
``(tof,
|
|
12
|
+
``(tof, axial, view, bin)``. For these, **view 0** is ``arr[0, :, 0, :]``.
|
|
13
13
|
- Raw Interfile projection loading in ``interfile_numpy`` is exposed as
|
|
14
|
-
``(
|
|
14
|
+
``(view, axial, bin)`` when no singleton TOF axis is present.
|
|
15
15
|
- ``PyTomographySimindAdaptor`` public object-space tensors are ``(x, y, z)``.
|
|
16
16
|
- PyTomography object space is ``(x, y, z)`` (``Lx, Ly, Lz``).
|
|
17
17
|
- PyTomography SIMIND projections from ``pytomography.io.SPECT.simind`` are
|
|
@@ -35,10 +35,31 @@ Units
|
|
|
35
35
|
- STIR/SIRF image geometry is typically expressed in **mm**.
|
|
36
36
|
- Connectors handle conversion internally via voxel-size settings (for example,
|
|
37
37
|
runtime switch ``PX`` is set in cm for SIMIND).
|
|
38
|
-
- STIR/SIRF
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
- STIR/SIRF adaptors read the voxel spacing in mm and need cubic voxels; other
|
|
39
|
+
images raise a ``ValueError``. Images report ``voxel_sizes()`` as
|
|
40
|
+
``(z, y, x)``, and raw ``get_grid_spacing()`` sequences follow the STIR
|
|
41
|
+
coordinate layout ``(unused, z, y, x)``.
|
|
42
|
+
|
|
43
|
+
Projection Size
|
|
44
|
+
---------------
|
|
45
|
+
|
|
46
|
+
The projection bin size and matrix come from the configuration: Index 28 (bin
|
|
47
|
+
size in cm), Index 76 (bins) and Index 77 (axial rows). Configuring a voxel
|
|
48
|
+
phantom does not change them, so set them to cover the phantom; ``run()`` warns
|
|
49
|
+
when the phantom is wider or longer than the projection field of view. The
|
|
50
|
+
matrix does not have to be square.
|
|
51
|
+
|
|
52
|
+
Some projectors need the projections sampled like the image. STIR's SPECTUB
|
|
53
|
+
needs equal axial sampling, so the OSEM examples set Index 28 to the voxel size.
|
|
54
|
+
|
|
55
|
+
Voxel Phantom Slices
|
|
56
|
+
--------------------
|
|
57
|
+
|
|
58
|
+
Image slices do not have to be square. The columns (x, Index 78 and 79) and
|
|
59
|
+
rows (y, Index 81 and 82) of the source and density maps are set separately,
|
|
60
|
+
and a SIMIND test checks that non-square slices give the same projections as
|
|
61
|
+
the same slices padded to square. The voxels themselves must be square in-plane:
|
|
62
|
+
SIMIND takes a single voxel side (Index 31 and the ``PX`` switch).
|
|
42
63
|
|
|
43
64
|
Example Configuration Guardrails
|
|
44
65
|
--------------------------------
|
|
@@ -50,6 +71,8 @@ are reproducible:
|
|
|
50
71
|
- ``config[29]=24`` for projection count.
|
|
51
72
|
- ``config[53]=0`` to keep collimator modeling geometric-only in these tests.
|
|
52
73
|
- ``config[19]=2`` to keep a consistent mapping used by current examples.
|
|
74
|
+
- ``config[28]=0.4``, ``config[76]=32`` and ``config[77]=32`` to put the
|
|
75
|
+
projections on the 4 mm, 32-voxel reconstruction grid.
|
|
53
76
|
|
|
54
77
|
Debug Checklist
|
|
55
78
|
---------------
|
|
@@ -188,6 +188,10 @@ def main() -> None:
|
|
|
188
188
|
adaptor.add_config_value(1, 140.0)
|
|
189
189
|
adaptor.add_config_value(19, 2)
|
|
190
190
|
adaptor.add_config_value(53, 0)
|
|
191
|
+
# Projections on the reconstruction grid: 32 x 32 bins of 4 mm (Index 28 in cm).
|
|
192
|
+
adaptor.add_config_value(28, 0.4)
|
|
193
|
+
adaptor.add_config_value(76, 32)
|
|
194
|
+
adaptor.add_config_value(77, 32)
|
|
191
195
|
adaptor.run()
|
|
192
196
|
|
|
193
197
|
projection = adaptor.get_total_output(window=1)
|
|
@@ -170,6 +170,10 @@ def main() -> None:
|
|
|
170
170
|
adaptor.add_config_value(1, 140.0)
|
|
171
171
|
adaptor.add_config_value(19, 2)
|
|
172
172
|
adaptor.add_config_value(53, 0)
|
|
173
|
+
# Projections on the reconstruction grid: 32 x 32 bins of 4 mm (Index 28 in cm).
|
|
174
|
+
adaptor.add_config_value(28, 0.4)
|
|
175
|
+
adaptor.add_config_value(76, 32)
|
|
176
|
+
adaptor.add_config_value(77, 32)
|
|
173
177
|
adaptor.run()
|
|
174
178
|
|
|
175
179
|
projection = adaptor.get_total_output(window=1)
|
|
@@ -90,21 +90,13 @@ def _build_system_matrix(
|
|
|
90
90
|
) -> SPECTSystemMatrix:
|
|
91
91
|
object_meta, proj_meta = pytomo_simind.get_metadata(str(projection_header))
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
except Exception:
|
|
101
|
-
pass
|
|
102
|
-
|
|
103
|
-
try:
|
|
104
|
-
psf_meta = pytomo_simind.get_psfmeta_from_header(str(projection_header))
|
|
105
|
-
obj2obj_transforms.append(SPECTPSFTransform(psf_meta=psf_meta))
|
|
106
|
-
except Exception:
|
|
107
|
-
pass
|
|
93
|
+
psf_meta = pytomo_simind.get_psfmeta_from_header(str(projection_header))
|
|
94
|
+
obj2obj_transforms = [
|
|
95
|
+
SPECTAttenuationTransform(
|
|
96
|
+
attenuation_map=attenuation_map_xyz.to(dtype=torch.float32).contiguous()
|
|
97
|
+
),
|
|
98
|
+
SPECTPSFTransform(psf_meta=psf_meta),
|
|
99
|
+
]
|
|
108
100
|
|
|
109
101
|
return SPECTSystemMatrix(
|
|
110
102
|
obj2obj_transforms=obj2obj_transforms,
|
|
@@ -144,6 +136,10 @@ def main() -> None:
|
|
|
144
136
|
adaptor.add_config_value(1, 140.0)
|
|
145
137
|
adaptor.add_config_value(19, 2)
|
|
146
138
|
adaptor.add_config_value(53, 0)
|
|
139
|
+
# Projections on the reconstruction grid: 32 x 32 bins of 4 mm (Index 28 in cm).
|
|
140
|
+
adaptor.add_config_value(28, 0.4)
|
|
141
|
+
adaptor.add_config_value(76, 32)
|
|
142
|
+
adaptor.add_config_value(77, 32)
|
|
147
143
|
adaptor.run()
|
|
148
144
|
|
|
149
145
|
projection_tensor = adaptor.get_total_output(window=1).to(dtype=torch.float32)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
import shutil
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
@@ -8,9 +9,9 @@ from simind_python_connector import SimindPythonConnector
|
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def require_simind() -> None:
|
|
11
|
-
if shutil.which("simind") is None:
|
|
12
|
+
if shutil.which(os.environ.get("SIMIND_BIN") or "simind") is None:
|
|
12
13
|
raise RuntimeError(
|
|
13
|
-
"SIMIND executable not found
|
|
14
|
+
"SIMIND executable not found ($SIMIND_BIN or simind on PATH). "
|
|
14
15
|
"Install SIMIND before running this example."
|
|
15
16
|
)
|
|
16
17
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=
|
|
2
|
+
requires = ["setuptools>=77.0.3", "wheel"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simind-python-connector"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.1.1"
|
|
8
8
|
description = "Python SIMIND Monte Carlo connector with STIR/SIRF/PyTomography adaptors"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -19,6 +19,7 @@ Usage:
|
|
|
19
19
|
|
|
20
20
|
import importlib
|
|
21
21
|
import logging
|
|
22
|
+
import os
|
|
22
23
|
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
23
24
|
|
|
24
25
|
from .base import AcquisitionDataInterface, ImageDataInterface
|
|
@@ -233,6 +234,9 @@ def create_image_data(
|
|
|
233
234
|
if isinstance(filepath_or_object, ImageDataInterface):
|
|
234
235
|
return filepath_or_object
|
|
235
236
|
|
|
237
|
+
if isinstance(filepath_or_object, os.PathLike):
|
|
238
|
+
filepath_or_object = os.fspath(filepath_or_object)
|
|
239
|
+
|
|
236
240
|
backend_hint = detect_image_backend(filepath_or_object)
|
|
237
241
|
if backend_hint is None:
|
|
238
242
|
backend_hint = detect_backend_from_interface(filepath_or_object)
|
|
@@ -314,6 +318,9 @@ def create_acquisition_data(
|
|
|
314
318
|
if isinstance(filepath_or_object, AcquisitionDataInterface):
|
|
315
319
|
return filepath_or_object
|
|
316
320
|
|
|
321
|
+
if isinstance(filepath_or_object, os.PathLike):
|
|
322
|
+
filepath_or_object = os.fspath(filepath_or_object)
|
|
323
|
+
|
|
317
324
|
backend_hint = detect_acquisition_backend(filepath_or_object)
|
|
318
325
|
if backend_hint is None:
|
|
319
326
|
backend_hint = detect_backend_from_interface(filepath_or_object)
|
|
@@ -88,7 +88,7 @@ class SirfImageData(ImageDataInterface):
|
|
|
88
88
|
|
|
89
89
|
def maximum(self, value: float) -> None:
|
|
90
90
|
"""Apply element-wise maximum."""
|
|
91
|
-
self._obj.maximum(value)
|
|
91
|
+
self._obj.maximum(value, out=self._obj)
|
|
92
92
|
|
|
93
93
|
@property
|
|
94
94
|
def native_object(self):
|
|
@@ -150,7 +150,7 @@ class SirfImageData(ImageDataInterface):
|
|
|
150
150
|
SirfImageData: self after modification (for chaining)
|
|
151
151
|
"""
|
|
152
152
|
y_obj = y._obj if isinstance(y, SirfImageData) else y
|
|
153
|
-
self._obj.sapyb(a, y_obj, b)
|
|
153
|
+
self._obj.sapyb(a, y_obj, b, out=self._obj)
|
|
154
154
|
return self
|
|
155
155
|
|
|
156
156
|
|
|
@@ -305,5 +305,5 @@ class SirfAcquisitionData(AcquisitionDataInterface):
|
|
|
305
305
|
SirfAcquisitionData: self after modification (for chaining)
|
|
306
306
|
"""
|
|
307
307
|
y_obj = y._obj if isinstance(y, SirfAcquisitionData) else y
|
|
308
|
-
self._obj.sapyb(a, y_obj, b)
|
|
308
|
+
self._obj.sapyb(a, y_obj, b, out=self._obj)
|
|
309
309
|
return self
|