dicomforge 0.6.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.
- dicomforge-0.6.0/.claude/settings.local.json +11 -0
- dicomforge-0.6.0/.github/workflows/ci.yml +26 -0
- dicomforge-0.6.0/.gitignore +17 -0
- dicomforge-0.6.0/CHANGELOG.md +115 -0
- dicomforge-0.6.0/CITATION.cff +38 -0
- dicomforge-0.6.0/CONTRIBUTING.md +31 -0
- dicomforge-0.6.0/LICENSE +21 -0
- dicomforge-0.6.0/PKG-INFO +273 -0
- dicomforge-0.6.0/README.md +208 -0
- dicomforge-0.6.0/SECURITY.md +33 -0
- dicomforge-0.6.0/docs/architecture.md +84 -0
- dicomforge-0.6.0/docs/compatibility.md +80 -0
- dicomforge-0.6.0/docs/conformance.md +194 -0
- dicomforge-0.6.0/docs/roadmap.md +166 -0
- dicomforge-0.6.0/docs/safety.md +28 -0
- dicomforge-0.6.0/examples/async_networking.py +17 -0
- dicomforge-0.6.0/examples/deidentify_with_report.py +19 -0
- dicomforge-0.6.0/examples/dicomweb_client.py +11 -0
- dicomforge-0.6.0/examples/end_to_end_workflow.py +113 -0
- dicomforge-0.6.0/examples/pixel_safety.py +22 -0
- dicomforge-0.6.0/examples/pydicom_io.py +6 -0
- dicomforge-0.6.0/pyproject.toml +52 -0
- dicomforge-0.6.0/src/dicomforge/__init__.py +143 -0
- dicomforge-0.6.0/src/dicomforge/adapt.py +514 -0
- dicomforge-0.6.0/src/dicomforge/anonymize.py +363 -0
- dicomforge-0.6.0/src/dicomforge/api.py +451 -0
- dicomforge-0.6.0/src/dicomforge/codecs.py +83 -0
- dicomforge-0.6.0/src/dicomforge/dataset.py +113 -0
- dicomforge-0.6.0/src/dicomforge/dicomweb.py +577 -0
- dicomforge-0.6.0/src/dicomforge/errors.py +21 -0
- dicomforge-0.6.0/src/dicomforge/io.py +192 -0
- dicomforge-0.6.0/src/dicomforge/network.py +597 -0
- dicomforge-0.6.0/src/dicomforge/pixels.py +420 -0
- dicomforge-0.6.0/src/dicomforge/tags.py +305 -0
- dicomforge-0.6.0/src/dicomforge/transfer_syntax.py +148 -0
- dicomforge-0.6.0/src/dicomforge/uids.py +108 -0
- dicomforge-0.6.0/tests/__init__.py +1 -0
- dicomforge-0.6.0/tests/test_adapt.py +333 -0
- dicomforge-0.6.0/tests/test_anonymize.py +169 -0
- dicomforge-0.6.0/tests/test_api.py +358 -0
- dicomforge-0.6.0/tests/test_core.py +139 -0
- dicomforge-0.6.0/tests/test_dicomweb.py +258 -0
- dicomforge-0.6.0/tests/test_io.py +113 -0
- dicomforge-0.6.0/tests/test_network.py +286 -0
- dicomforge-0.6.0/tests/test_pixels.py +264 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(grep -E \"\\\\.\\(py|toml|txt|cfg|ini|md|yml|yaml\\)$|Makefile|^.*\\\\.github.*$\")",
|
|
5
|
+
"Bash(PYTHONPATH=src python3 -m unittest discover -v)",
|
|
6
|
+
"Bash(python3 -m compileall -q src tests)",
|
|
7
|
+
"Bash(PYTHONPATH=src python3 -W error::DeprecationWarning -m unittest)",
|
|
8
|
+
"Bash(PYTHONPATH=src python3 -m unittest)"
|
|
9
|
+
]
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
name: Python ${{ matrix.python-version }}
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
- name: Install package
|
|
22
|
+
run: python -m pip install -e ".[dev]"
|
|
23
|
+
- name: Unit tests
|
|
24
|
+
run: PYTHONPATH=src python -m unittest
|
|
25
|
+
- name: Compile check
|
|
26
|
+
run: PYTHONPATH=src python -m compileall -q src tests
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to DicomForge are documented here.
|
|
4
|
+
Versions follow [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [0.6.0] — 2026-05-05
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- `dicomforge.adapt` — adoption-layer integration adapters:
|
|
10
|
+
- `from_pydicom` / `to_pydicom` — bidirectional pydicom Dataset conversion
|
|
11
|
+
- `pixel_array` — numpy array from uncompressed PixelData with correct dtype
|
|
12
|
+
- `to_pil_image` — PIL Image with automatic VOI windowing and MONOCHROME1 inversion
|
|
13
|
+
- `to_json` / `from_json` — DICOM JSON Model round-trip
|
|
14
|
+
- `from_pynetdicom_event` — extract DicomDataset from a pynetdicom event
|
|
15
|
+
- `dicomforge.api` — high-level convenience API:
|
|
16
|
+
- `DicomFile` — lazy-loading file wrapper with named property access for 30+ tags
|
|
17
|
+
- `quick_anonymize` — read → de-identify → write in one call
|
|
18
|
+
- `validate_dataset` — structural validation with human-readable issue list
|
|
19
|
+
- `batch_anonymize` — anonymize a file list; partial failures are isolated
|
|
20
|
+
- 55 additional `Tag` keywords (SeriesNumber, BodyPartExamined, PixelSpacing,
|
|
21
|
+
ImagePositionPatient, Manufacturer, AttendingPhysicianName, and more)
|
|
22
|
+
- 20+ additional `SopClassUID` constants (PET, NM, US, RT, SR, WSI, enhanced CT/MR)
|
|
23
|
+
- 6 additional `TransferSyntaxUID` constants (JPEG 2000 lossy, JPEG-LS near-lossless,
|
|
24
|
+
HT-JPEG 2000 lossless and lossy)
|
|
25
|
+
- 6 additional registered `TransferSyntax` entries
|
|
26
|
+
- `network` and `all` optional dependency extras in `pyproject.toml`
|
|
27
|
+
- `DicomDataset.copy()` and `DicomDataset.__repr__`
|
|
28
|
+
- `Tag.__repr__` returns keyword name when available (e.g. `Tag.PatientName`)
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- `AnonymizationPlan.starter_profile` expanded from 27 to 48 de-identification rules,
|
|
32
|
+
adding patient weight/size/comments, ethnic group, smoking/pregnancy status,
|
|
33
|
+
attending/requesting physician, device serial number, and department name
|
|
34
|
+
- `UidRemapper` is now thread-safe (internal cache protected by `threading.Lock`)
|
|
35
|
+
- `UidRemapper` now also remaps `MediaStorageSOPInstanceUID` and `ReferencedSOPInstanceUID`
|
|
36
|
+
- `default_registry()` now returns a cached singleton instead of a new instance per call
|
|
37
|
+
- `io.write` prefers `pydicom.dcmwrite()` on pydicom ≥ 3.0 (avoids deprecation warning)
|
|
38
|
+
- Network `_read_message` and `_write_message` now enforce a 30-second timeout and a
|
|
39
|
+
64 MiB maximum message size
|
|
40
|
+
- All public pixel helper functions (`rescale_values`, `is_monochrome`, `voi_window_bounds`,
|
|
41
|
+
`apply_voi_window_from_dataset`, etc.) are now exported from `dicomforge` top level
|
|
42
|
+
- `pyproject.toml` version bumped to 0.6.0; development status promoted to Beta
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
- `assert_pixel_data_length` incorrectly validated the last byte of even-length
|
|
46
|
+
pixel data as a padding byte, causing `PixelMetadataError` for any dataset
|
|
47
|
+
whose last pixel value was non-zero
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## [0.5.0] — 2025-01-01
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
- `dicomforge.dicomweb` — dependency-free DICOMweb client:
|
|
55
|
+
- QIDO-RS query builder (`QidoQuery`)
|
|
56
|
+
- WADO-RS study/series/instance retrieval
|
|
57
|
+
- STOW-RS multipart upload
|
|
58
|
+
- DICOM JSON Model conversion (`dataset_from_dicom_json`, `dataset_to_dicom_json`)
|
|
59
|
+
- `parse_multipart_related` / `build_multipart_related`
|
|
60
|
+
- Injectable `DicomwebTransport` protocol with stdlib `UrllibDicomwebTransport`
|
|
61
|
+
- `dicomforge.network` — async DIMSE-style services:
|
|
62
|
+
- `Association` client with C-ECHO, C-FIND, C-MOVE, C-STORE
|
|
63
|
+
- `DimseServer` SCP with backpressure-aware C-STORE queue
|
|
64
|
+
- `open_association` / `start_dimse_server` convenience helpers
|
|
65
|
+
- `AssociationRejectedError`, `AssociationClosedError`
|
|
66
|
+
- DICOMweb integration: `dataset_to_message` / `dataset_from_message` with
|
|
67
|
+
base64-encoded binary and nested dataset support
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## [0.4.0] — 2024-10-01
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
- `dicomforge.network` initial implementation (async association lifecycle,
|
|
75
|
+
C-ECHO, JSON framing)
|
|
76
|
+
- `DimseStatus` with class-level constants (SUCCESS, PENDING, CANCEL, UNABLE_TO_PROCESS)
|
|
77
|
+
- `AssociationRequest` frozen dataclass
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## [0.3.0] — 2024-07-01
|
|
82
|
+
|
|
83
|
+
### Added
|
|
84
|
+
- `dicomforge.anonymize` — de-identification engine:
|
|
85
|
+
- `AnonymizationPlan` with `starter_profile()` and `basic_profile()` factory methods
|
|
86
|
+
- `UidRemapper` with SHA-256 deterministic remapping
|
|
87
|
+
- `AnonymizationReport` / `AnonymizationEvent` audit trail
|
|
88
|
+
- `PrivateTagAction` (REMOVE / KEEP)
|
|
89
|
+
- Recursive sequence processing
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## [0.2.0] — 2024-04-01
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
- `dicomforge.pixels` — pixel metadata and safety layer:
|
|
97
|
+
- `FrameMetadata` with `from_dataset` and eager validation
|
|
98
|
+
- `check_pixel_capability` — metadata + codec pre-flight check
|
|
99
|
+
- `PixelCapability`, `VoiLut`
|
|
100
|
+
- `rescale_value`, `apply_voi_window`, photometric interpretation helpers
|
|
101
|
+
- `dicomforge.io` — optional pydicom read/write backend
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## [0.1.0] — 2024-01-01
|
|
106
|
+
|
|
107
|
+
### Added
|
|
108
|
+
- `dicomforge.tags` — `Tag` frozen dataclass with keyword registry and multi-format parser
|
|
109
|
+
- `dicomforge.dataset` — `DicomDataset` (MutableMapping with tag normalization)
|
|
110
|
+
- `dicomforge.transfer_syntax` — `TransferSyntax` registry with safe unknown defaults
|
|
111
|
+
- `dicomforge.codecs` — `CodecRegistry` and `Codec` capability model
|
|
112
|
+
- `dicomforge.errors` — `DicomForgeError` hierarchy
|
|
113
|
+
- `dicomforge.uids` — `SopClassUID`, `TransferSyntaxUID`, `ImplementationUID`, `DimseStatusCode`
|
|
114
|
+
- CI matrix: Python 3.9–3.13 on GitHub Actions
|
|
115
|
+
- MIT license, CONTRIBUTING.md, SECURITY.md, architecture and conformance docs
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use DicomForge in your research, please cite it as below."
|
|
3
|
+
type: software
|
|
4
|
+
title: "DicomForge: A Lightweight Python Toolkit for Typed DICOM Processing and Medical Imaging Workflows"
|
|
5
|
+
version: 0.6.0
|
|
6
|
+
date-released: "2026-05-05"
|
|
7
|
+
license: MIT
|
|
8
|
+
repository-code: "https://github.com/dicomforge/dicomforge"
|
|
9
|
+
url: "https://github.com/dicomforge/dicomforge"
|
|
10
|
+
abstract: >-
|
|
11
|
+
DicomForge is a typed, dependency-free Python library for DICOM processing.
|
|
12
|
+
It provides tag normalization, transfer syntax classification, pixel metadata
|
|
13
|
+
validation, auditable de-identification with deterministic UID remapping,
|
|
14
|
+
DICOMweb client helpers, and an adoption layer for pydicom, NumPy, and Pillow
|
|
15
|
+
integration — designed for both hobby medical imaging projects and commercial
|
|
16
|
+
production software.
|
|
17
|
+
keywords:
|
|
18
|
+
- DICOM
|
|
19
|
+
- medical imaging
|
|
20
|
+
- radiology
|
|
21
|
+
- de-identification
|
|
22
|
+
- DICOMweb
|
|
23
|
+
- healthcare
|
|
24
|
+
- Python
|
|
25
|
+
authors:
|
|
26
|
+
- family-names: Merchant
|
|
27
|
+
given-names: Mustufa
|
|
28
|
+
affiliation: "Kub Technologies Inc."
|
|
29
|
+
preferred-citation:
|
|
30
|
+
type: article
|
|
31
|
+
title: "DicomForge: A Lightweight Python Toolkit for Typed DICOM Processing and Medical Imaging Workflows"
|
|
32
|
+
authors:
|
|
33
|
+
- family-names: Merchant
|
|
34
|
+
given-names: Mustufa
|
|
35
|
+
affiliation: "Kub Technologies Inc."
|
|
36
|
+
journal: "Journal of Open Source Software"
|
|
37
|
+
year: 2026
|
|
38
|
+
url: "https://github.com/dicomforge/dicomforge"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for helping make DICOMForge safer and more useful.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python3 -m venv .venv
|
|
9
|
+
. .venv/bin/activate
|
|
10
|
+
python -m pip install -e ".[dev]"
|
|
11
|
+
PYTHONPATH=src python -m unittest
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Project Rules
|
|
15
|
+
|
|
16
|
+
- Keep the core package dependency-light.
|
|
17
|
+
- Prefer explicit DICOM boundaries over claiming unsupported conformance.
|
|
18
|
+
- Add tests for every user-visible behavior change.
|
|
19
|
+
- Do not commit real patient data or DICOM files containing PHI.
|
|
20
|
+
- Prefer small, focused modules over broad abstractions.
|
|
21
|
+
|
|
22
|
+
## DICOM Changes
|
|
23
|
+
|
|
24
|
+
When adding DICOM behavior, include one of:
|
|
25
|
+
|
|
26
|
+
- a reference to the DICOM part/table used
|
|
27
|
+
- a test that captures the expected behavior
|
|
28
|
+
- documentation in `docs/conformance.md` explaining the current boundary
|
|
29
|
+
|
|
30
|
+
Full clinical conformance work should also update the compatibility matrix and
|
|
31
|
+
conformance notes.
|
dicomforge-0.6.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DICOMForge contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dicomforge
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: A lightweight, typed Python DICOM processing toolkit for medical imaging.
|
|
5
|
+
Project-URL: Homepage, https://github.com/dicomforge/dicomforge
|
|
6
|
+
Project-URL: Repository, https://github.com/dicomforge/dicomforge
|
|
7
|
+
Project-URL: Documentation, https://github.com/dicomforge/dicomforge/tree/main/docs
|
|
8
|
+
Project-URL: Issues, https://github.com/dicomforge/dicomforge/issues
|
|
9
|
+
Author: DICOMForge contributors
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 DICOMForge contributors
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Keywords: dicom,healthcare,medical-imaging,pacs,radiology
|
|
33
|
+
Classifier: Development Status :: 4 - Beta
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: Intended Audience :: Healthcare Industry
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Programming Language :: Python :: 3
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
43
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
44
|
+
Requires-Python: >=3.9
|
|
45
|
+
Provides-Extra: all
|
|
46
|
+
Requires-Dist: numpy>=1.23; extra == 'all'
|
|
47
|
+
Requires-Dist: pillow>=10; extra == 'all'
|
|
48
|
+
Requires-Dist: pydicom>=2.4; extra == 'all'
|
|
49
|
+
Requires-Dist: pynetdicom>=2.0; extra == 'all'
|
|
50
|
+
Provides-Extra: dev
|
|
51
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
52
|
+
Requires-Dist: numpy>=1.23; extra == 'dev'
|
|
53
|
+
Requires-Dist: pillow>=10; extra == 'dev'
|
|
54
|
+
Requires-Dist: pydicom>=2.4; extra == 'dev'
|
|
55
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
56
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
57
|
+
Provides-Extra: network
|
|
58
|
+
Requires-Dist: pynetdicom>=2.0; extra == 'network'
|
|
59
|
+
Provides-Extra: pixels
|
|
60
|
+
Requires-Dist: numpy>=1.23; extra == 'pixels'
|
|
61
|
+
Requires-Dist: pillow>=10; extra == 'pixels'
|
|
62
|
+
Provides-Extra: pydicom
|
|
63
|
+
Requires-Dist: pydicom>=2.4; extra == 'pydicom'
|
|
64
|
+
Description-Content-Type: text/markdown
|
|
65
|
+
|
|
66
|
+
# DICOMForge
|
|
67
|
+
|
|
68
|
+
DICOMForge is a Python DICOM processing library for medical imaging
|
|
69
|
+
applications. The goal is a lightweight core with typed, predictable APIs,
|
|
70
|
+
explicit safety boundaries, and optional integrations for heavier work such as
|
|
71
|
+
pixel codecs, wire-compatible networking, and DICOMweb.
|
|
72
|
+
|
|
73
|
+
This repository is intentionally starting with a small, solid core:
|
|
74
|
+
|
|
75
|
+
- typed tags and value access
|
|
76
|
+
- transfer syntax classification
|
|
77
|
+
- pluggable codec registry
|
|
78
|
+
- de-identification profiles, deterministic UID remapping, and audit reports
|
|
79
|
+
- pixel metadata and safety checks
|
|
80
|
+
- VOI window, rescale, and photometric interpretation helpers
|
|
81
|
+
- async networking primitives for association lifecycle and DIMSE-style commands
|
|
82
|
+
- DICOMweb query, retrieval, upload, and multipart helpers
|
|
83
|
+
- optional `pydicom` IO backend
|
|
84
|
+
- standard-library tests
|
|
85
|
+
|
|
86
|
+
## Why another DICOM library?
|
|
87
|
+
|
|
88
|
+
The adoption target is not just feature count. The library should feel safe for
|
|
89
|
+
production teams and approachable for new medical-imaging developers.
|
|
90
|
+
|
|
91
|
+
Design priorities:
|
|
92
|
+
|
|
93
|
+
- **Small import surface:** core imports should not pull in NumPy, Pillow, codec
|
|
94
|
+
wheels, or networking stacks.
|
|
95
|
+
- **Typed access:** avoid stringly-typed dataset code where common attributes can
|
|
96
|
+
be accessed predictably.
|
|
97
|
+
- **Explicit codec model:** make unsupported transfer syntaxes visible before a
|
|
98
|
+
transcoding job fails halfway through.
|
|
99
|
+
- **Lazy IO:** support large studies and multi-frame objects without forcing
|
|
100
|
+
full pixel loading.
|
|
101
|
+
- **Character-set correctness:** treat text encoding as a first-class concern.
|
|
102
|
+
- **Concurrency-safe services:** design networking and DICOMweb APIs around
|
|
103
|
+
explicit lifecycle and backpressure.
|
|
104
|
+
- **Good errors:** explain the DICOM concept, the offending tag, and the next
|
|
105
|
+
action where possible.
|
|
106
|
+
|
|
107
|
+
See [docs/architecture.md](docs/architecture.md) and
|
|
108
|
+
[docs/roadmap.md](docs/roadmap.md). Current implementation boundaries are
|
|
109
|
+
tracked in [docs/conformance.md](docs/conformance.md). For repository naming
|
|
110
|
+
and discoverability notes, see [docs/branding.md](docs/branding.md).
|
|
111
|
+
|
|
112
|
+
## Commercial Readiness
|
|
113
|
+
|
|
114
|
+
DICOMForge is MIT licensed and designed for commercial use as a developer
|
|
115
|
+
library. It is not a medical device, diagnostic application, complete PS3.15
|
|
116
|
+
de-identification engine, or wire-compatible DIMSE implementation. See
|
|
117
|
+
[docs/safety.md](docs/safety.md), [docs/conformance.md](docs/conformance.md),
|
|
118
|
+
and [docs/compatibility.md](docs/compatibility.md) before using it in regulated
|
|
119
|
+
clinical workflows.
|
|
120
|
+
|
|
121
|
+
## When To Use It
|
|
122
|
+
|
|
123
|
+
Use DICOMForge when you need:
|
|
124
|
+
|
|
125
|
+
- typed, dependency-light DICOM metadata handling
|
|
126
|
+
- pixel metadata validation before decoding or processing
|
|
127
|
+
- de-identification planning with deterministic UID remapping and audit reports
|
|
128
|
+
- pydicom-backed file IO behind a smaller application API
|
|
129
|
+
- DICOMweb URL/query, DICOM JSON, STOW multipart, and response parsing helpers
|
|
130
|
+
- async lifecycle and backpressure primitives for DICOM-like service design
|
|
131
|
+
|
|
132
|
+
Do not use DICOMForge as the only component for:
|
|
133
|
+
|
|
134
|
+
- diagnostic interpretation or medical-device behavior
|
|
135
|
+
- legal de-identification approval without site policy and human review
|
|
136
|
+
- direct DIMSE/PACS interoperability over the DICOM Upper Layer
|
|
137
|
+
- full-fidelity DICOM editing that requires every VR, character set, and IOD rule
|
|
138
|
+
- replacing pydicom, pynetdicom, or integration-tested PACS/VNA validation
|
|
139
|
+
|
|
140
|
+
## Architecture At A Glance
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
dicomforge.tags typed tag parsing and common keyword constants
|
|
144
|
+
dicomforge.dataset lightweight mutable dataset wrapper
|
|
145
|
+
dicomforge.transfer_syntax transfer syntax classification
|
|
146
|
+
dicomforge.codecs codec capability registry
|
|
147
|
+
dicomforge.pixels pixel metadata safety checks and small value helpers
|
|
148
|
+
dicomforge.anonymize starter de-identification plans and audit reports
|
|
149
|
+
dicomforge.io optional pydicom read/write adapter
|
|
150
|
+
dicomforge.network async command lifecycle primitives, not DICOM UL PDUs
|
|
151
|
+
dicomforge.dicomweb QIDO/WADO/STOW helpers with injectable HTTP transport
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## API Stability
|
|
155
|
+
|
|
156
|
+
DICOMForge is pre-1.0. Public APIs are intended to be small and stable, but
|
|
157
|
+
breaking changes may happen while the library moves toward a 1.0 adoption bar.
|
|
158
|
+
Breaking changes should be documented in release notes and paired with migration
|
|
159
|
+
guidance.
|
|
160
|
+
|
|
161
|
+
## Quick Start
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from dicomforge import DicomDataset, Tag, TransferSyntax
|
|
165
|
+
|
|
166
|
+
ds = DicomDataset()
|
|
167
|
+
ds.set(Tag.PatientName, "Anonymous")
|
|
168
|
+
ds.set(Tag.Modality, "CT")
|
|
169
|
+
|
|
170
|
+
syntax = TransferSyntax.from_uid("1.2.840.10008.1.2.1")
|
|
171
|
+
assert syntax.is_little_endian
|
|
172
|
+
assert syntax.is_explicit_vr
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Optional pydicom-backed reading:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from dicomforge.io import read
|
|
179
|
+
|
|
180
|
+
dataset = read("image.dcm", stop_before_pixels=True)
|
|
181
|
+
print(dataset.get("PatientName"))
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Basic de-identification:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from dicomforge import AnonymizationPlan, DicomDataset, PrivateTagAction
|
|
188
|
+
|
|
189
|
+
dataset = DicomDataset(
|
|
190
|
+
{
|
|
191
|
+
"PatientName": "Ada Lovelace",
|
|
192
|
+
"PatientID": "MRN-123",
|
|
193
|
+
"StudyInstanceUID": "1.2.826.0.1.3680043.8.498.1",
|
|
194
|
+
(0x0011, 0x1001): "private vendor value",
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
plan = AnonymizationPlan.starter_profile(
|
|
199
|
+
uid_salt="project-specific-secret",
|
|
200
|
+
private_tag_action=PrivateTagAction.REMOVE,
|
|
201
|
+
)
|
|
202
|
+
report = plan.apply_with_report(dataset)
|
|
203
|
+
|
|
204
|
+
assert dataset.get("PatientName") == "Anonymous"
|
|
205
|
+
assert dataset.get("PatientIdentityRemoved") == "YES"
|
|
206
|
+
assert report.private_tags_removed == 1
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Async networking:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from dicomforge.network import DimseServer, open_association
|
|
213
|
+
|
|
214
|
+
async with DimseServer(ae_title="LOCAL-SCP") as server:
|
|
215
|
+
async with await open_association(
|
|
216
|
+
"127.0.0.1",
|
|
217
|
+
server.bound_port,
|
|
218
|
+
called_ae_title="LOCAL-SCP",
|
|
219
|
+
) as association:
|
|
220
|
+
status = await association.c_echo()
|
|
221
|
+
assert status.is_success
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
DICOMweb query building:
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
from dicomforge.dicomweb import DicomwebClient, QidoQuery, UrllibDicomwebTransport
|
|
228
|
+
|
|
229
|
+
client = DicomwebClient(
|
|
230
|
+
"https://pacs.example/dicomweb",
|
|
231
|
+
UrllibDicomwebTransport(timeout=10),
|
|
232
|
+
)
|
|
233
|
+
studies = client.search_studies(QidoQuery().patient_id("MRN-123").modality("CT"))
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## De-identification Scope
|
|
237
|
+
|
|
238
|
+
DICOMForge implements a practical, conservative subset of the DICOM PS3.15
|
|
239
|
+
Basic Application Level Confidentiality Profile for non-pixel attributes:
|
|
240
|
+
common patient, accession, date/time, institution, operator, and UID fields are
|
|
241
|
+
removed, emptied, replaced, or deterministically remapped. Private tag handling
|
|
242
|
+
is explicit and defaults to removal. `remove_private_tags` on `apply` and
|
|
243
|
+
`apply_with_report` remains available as a per-call override for older callers.
|
|
244
|
+
|
|
245
|
+
This is a software library, not a compliance certificate. Production use should
|
|
246
|
+
pair it with site-specific policy, legal review, image pixel review, and a risk
|
|
247
|
+
assessment for the data release context.
|
|
248
|
+
|
|
249
|
+
## Development
|
|
250
|
+
|
|
251
|
+
Run the standard-library test suite:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
PYTHONPATH=src python3 -m unittest
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Optional checks used by maintainers when development dependencies are installed:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
python -m ruff check src tests
|
|
261
|
+
python -m mypy src/dicomforge
|
|
262
|
+
python -m compileall -q src tests
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Examples live in [examples](examples).
|
|
266
|
+
|
|
267
|
+
For a fuller commercial workflow, see
|
|
268
|
+
[examples/end_to_end_workflow.py](examples/end_to_end_workflow.py).
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
DICOMForge is distributed under the MIT License for personal and commercial
|
|
273
|
+
use. See [LICENSE](LICENSE).
|