Skip to content

Commit

Permalink
Merge branch 'master' into feature/labelmap
Browse files Browse the repository at this point in the history
  • Loading branch information
CPBridge committed Sep 30, 2024
2 parents 5d77c95 + cb62737 commit 8bf2a24
Show file tree
Hide file tree
Showing 57 changed files with 4,063 additions and 1,246 deletions.
2 changes: 1 addition & 1 deletion .codespellrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[codespell]
skip = .git,*.pdf,*.svg,*.ipynb
# te,fo - either abbreviations of variables
ignore-words-list = te,fo
ignore-words-list = te,fo,socio-economic
4 changes: 2 additions & 2 deletions .github/workflows/run_unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12"]
dependencies: [".", "'.[libjpeg]'"]

steps:
Expand All @@ -27,7 +27,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools
pip install -r requirements_test.txt
pip install .[test]
pip install ${{ matrix.dependencies }}
- name: Lint with flake8
run: |
Expand Down
6 changes: 4 additions & 2 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ formats: all
# Optionally set the version of Python and requirements required to build your docs
python:
install:
- requirements: requirements_docs.txt
- path: .
- method: pip
path: .
extra_requirements:
- docs
18 changes: 7 additions & 11 deletions bin/create_iods_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,12 @@ def _create_modules(directory):
# https://github.com/innolitics/dicom-standard/tree/master/standard
try:
directory = sys.argv[1]
except IndexError:
raise ValueError('Path to directory must be provided.')
except IndexError as e:
raise ValueError('Path to directory must be provided.') from e
if not os.path.exists(directory):
raise OSError('Path does not exist: "{}"'.format(directory))
raise OSError(f'Path does not exist: "{directory}"')
if not os.path.isdir(directory):
raise OSError('Path is not a directory: "{}"'.format(directory))
raise OSError(f'Path is not a directory: "{directory}"')

now = datetime.datetime.now()
current_date = datetime.datetime.date(now).strftime('%Y-%m-%d')
Expand All @@ -127,13 +127,11 @@ def _create_modules(directory):
fp.write('\n\n')
iods_formatted = _dump_json(iods).replace('null', 'None')
fp.write(
'IOD_MODULE_MAP: Dict[str, List[Dict[str, str]]] = {}'.format(
iods_formatted
)
f'IOD_MODULE_MAP: Dict[str, List[Dict[str, str]]] = {iods_formatted}'
)
fp.write('\n\n')
sop_to_iods_formatted = _dump_json(sop_to_iods).replace('null', 'None')
fp.write('SOP_CLASS_UID_IOD_KEY_MAP = {}'.format(sop_to_iods_formatted))
fp.write(f'SOP_CLASS_UID_IOD_KEY_MAP = {sop_to_iods_formatted}')

modules = _create_modules(directory)
modules_docstr = '\n'.join([
Expand All @@ -148,7 +146,5 @@ def _create_modules(directory):
fp.write('\n\n')
modules_formatted = _dump_json(modules).replace('null', 'None')
fp.write(
'MODULE_ATTRIBUTE_MAP: Dict[str, List[Dict[str, Union[str, Sequence[str]]]]] = {}'.format( # noqa: E501
modules_formatted
)
f'MODULE_ATTRIBUTE_MAP: Dict[str, List[Dict[str, Union[str, Sequence[str]]]]] = {modules_formatted}' # noqa: E501
)
Binary file modified data/test_files/seg_image_sm_control.dcm
Binary file not shown.
Binary file modified data/test_files/seg_image_sm_dots.dcm
Binary file not shown.
Binary file modified data/test_files/seg_image_sm_dots_tiled_full.dcm
Binary file not shown.
Binary file modified data/test_files/seg_image_sm_numbers.dcm
Binary file not shown.
4 changes: 2 additions & 2 deletions docs/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Install requirements:

.. code-block:: none
pip install -r ~/highdicom/requirements_test.txt
pip install .[test]
Run tests (including checks for PEP8 compliance):

Expand All @@ -79,7 +79,7 @@ Install requirements:

.. code-block:: none
pip install -r ~/highdicom/requirements_docs.txt
pip install .[docs]
Build documentation in *HTML* format:

Expand Down
37 changes: 18 additions & 19 deletions docs/seg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -807,39 +807,38 @@ We recommend that if you do this, you specify ``max_fractional_value=1`` to
clearly communicate that the segmentation is inherently binary in nature.

Why would you want to make this seemingly rather strange choice? Well,
``"FRACTIONAL"`` SEGs tend to compress *much* better than ``"BINARY"`` ones
(see next section). Note however, that this is arguably an misuse of the intent
of the standard, so *caveat emptor*.
``"FRACTIONAL"`` SEGs tend to compress better than ``"BINARY"`` ones (see next
section). Note however, that this is arguably an misuse of the intent of the
standard, so *caveat emptor*. Also note that while this used to be a more
serious issue it is less serious now that ``"JPEG2000Lossless"`` compression is
now supported for ``"BINARY"`` segmentations as of highdicom v0.23.0.

Compression
-----------

The types of pixel compression available in segmentation images depends on the
segmentation type. Pixels in a ``"BINARY"`` segmentation image are "bit-packed"
such that 8 pixels are grouped into 1 byte in the stored array. If a given frame
contains a number of pixels that is not divisible by 8 exactly, a single byte
segmentation type.

Pixels in an uncompressed ``"BINARY"`` segmentation image are "bit-packed" such
that 8 pixels are grouped into 1 byte in the stored array. If a given frame
contains a number of pixels that is not divisible by 8 exactly, a single byte
will straddle a frame boundary into the next frame if there is one, or the byte
will be padded with zeroes of there are no further frames. This means that
retrieving individual frames from segmentation images in which each frame
size is not divisible by 8 becomes problematic. No further compression may be
applied to frames of ``"BINARY"`` segmentation images.
retrieving individual frames from segmentation images in which each frame size
is not divisible by 8 becomes problematic. For this reason, as well as for
space efficiency (sparse segmentations tend to compress very well), we
therefore strongly recommend using ``"JPEG2000Lossless"`` compression with
``"BINARY"`` segmentations. This is the only compression method currently
supported for ``"BINARY"`` segmentations. However, beware that reading these
single-bit JPEG 2000 images may not be supported by all other tools and
viewers.

Pixels in ``"FRACTIONAL"`` segmentation images may be compressed using one of
the lossless compression methods available within DICOM. Currently *highdicom*
supports the following compressed transfer syntaxes when creating
``"FRACTIONAL"`` segmentation images: ``"RLELossless"``,
``"JPEG2000Lossless"``, and ``"JPEGLSLossless"``.

Note that there may be advantages to using ``"FRACTIONAL"`` segmentations to
store segmentation images that are binary in nature (i.e. only taking values 0
and 1):

- If the segmentation is very simple or sparse, the lossless compression methods
available in ``"FRACTIONAL"`` images may be more effective than the
"bit-packing" method required by ``"BINARY"`` segmentations.
- The clear frame boundaries make retrieving individual frames from
``"FRACTIONAL"`` image files possible.

Multiprocessing
---------------

Expand Down
82 changes: 82 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"

[project]
name = "highdicom"
version = "0.23.0"
description = "High-level DICOM abstractions."
readme = "README.md"
requires-python = ">=3.10"
authors = [
{ name = "Markus D. Herrmann" },
]
maintainers = [
{ name = "Markus D. Herrmann" },
{ name = "Christopher P. Bridge" },
]
license = { text = "LICENSE" }
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Operating System :: MacOS",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Multimedia :: Graphics",
"Topic :: Scientific/Engineering :: Information Analysis",
]
dependencies = [
"numpy>=1.19",
"pillow>=8.3",
"pydicom>=3.0.1",
"pyjpegls>=1.0.0",
]

[project.optional-dependencies]
libjpeg = [
"pylibjpeg-libjpeg>=2.1",
"pylibjpeg-openjpeg>=2.0.0",
"pylibjpeg>=2.0",
]
test = [
"mypy==0.971",
"pytest==7.4.4",
"pytest-cov==4.1.0",
"pytest-flake8==1.1.1",
]
docs = [
"sphinx-autodoc-typehints==1.17.0",
"sphinx-pyreverse==0.0.17",
"sphinx-rtd-theme==1.0.0",
"sphinxcontrib-autoprogram==0.1.7",
"sphinxcontrib-websupport==1.2.4",
]

[project.urls]
homepage = "https://github.com/imagingdatacommons/highdicom"
documentation = "https://highdicom.readthedocs.io/"
repository = "https://github.com/ImagingDataCommons/highdicom.git"

[tool.pytest.ini_options]
minversion = "7"
addopts = ["--doctest-modules", "-ra", "--strict-config", "--strict-markers"]
testpaths = ["tests"]
log_cli_level = "INFO"
xfail_strict = true

[tool.mypy]
warn_unreachable = true
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]

[[tool.mypy.overrides]]
module = "mypy-pydicom.*"
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = "mypy-PIL.*"
ignore_missing_imports = true
5 changes: 0 additions & 5 deletions requirements_docs.txt

This file was deleted.

5 changes: 0 additions & 5 deletions requirements_test.txt

This file was deleted.

14 changes: 0 additions & 14 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,3 @@ test=pytest
max_line_length = 80
ignore = E121 E125 W504
statistics = True

[mypy]
warn_unreachable = True

[mypy-pydicom.*]
ignore_missing_imports = True

[mypy-PIL.*]
ignore_missing_imports = True

[tool:pytest]
python_files = tests/*.py
log_cli_level = INFO
addopts = --doctest-modules
64 changes: 0 additions & 64 deletions setup.py

This file was deleted.

10 changes: 5 additions & 5 deletions src/highdicom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
from highdicom.version import __version__

__all__ = [
'LUT',
'UID',
'VOILUT',
'AlgorithmIdentificationSequence',
'AnatomicalOrientationTypeValues',
'ContentCreatorIdentificationCodeSequence',
Expand All @@ -66,7 +69,6 @@
'DimensionOrganizationTypeValues',
'IssuerOfIdentifier',
'LateralityValues',
'LUT',
'ModalityLUT',
'ModalityLUTTransformation',
'PaletteColorLUT',
Expand All @@ -85,19 +87,18 @@
'PresentationLUTTransformation',
'ReferencedImageSequence',
'RescaleTypeValues',
'SOPClass',
'SegmentedPaletteColorLUT',
'SpecimenCollection',
'SpecimenDescription',
'SpecimenPreparationStep',
'SpecimenProcessing',
'SpecimenSampling',
'SpecimenStaining',
'SOPClass',
'UID',
'UniversalEntityIDTypeValues',
'VOILUT',
'VOILUTFunctionValues',
'VOILUTTransformation',
'__version__',
'ann',
'color',
'frame',
Expand All @@ -111,5 +112,4 @@
'spatial',
'sr',
'utils',
'__version__',
]
Loading

0 comments on commit 8bf2a24

Please sign in to comment.