Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Version 0.22.0 #269

Merged
merged 117 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
3b9204d
Make checks more efficient, avoid appending to pydicom.Sequence
CPBridge May 15, 2023
20cdadb
Add check for non-unique plane positions
CPBridge May 15, 2023
fc74493
Tidy up
CPBridge May 15, 2023
30f9dcc
Move compression out of the loop, in preparation for multiprocessing
CPBridge May 16, 2023
8b86a12
Simplify SegmentSequence logic
CPBridge May 16, 2023
1fa59f7
Factored out several parts of seg constructor into methods for readib…
CPBridge May 16, 2023
76be8bf
Add annotation_coordinate_type property and check for 0 in get_coordi…
CPBridge Jun 9, 2023
e7c6481
Add annread function
CPBridge Jun 9, 2023
3de6e18
Fix for single channel floating point pixel array
CPBridge Jun 13, 2023
92fa008
Merge branch 'master' into ann_parsing_improvements
CPBridge Jun 21, 2023
1cbd5cd
Update user guide
CPBridge Jun 21, 2023
3e1a687
Merge branch 'master' into seg_creation_efficiency
CPBridge Jun 21, 2023
552ae63
Codespell typo
CPBridge Jun 21, 2023
e279ece
Merge pull request #230 from ImagingDataCommons/ann_parsing_improvements
CPBridge Jun 22, 2023
4440f64
Apply suggestions from code review
CPBridge Jun 22, 2023
5f66eb3
Simplification of _get_pixel_measures
CPBridge Jul 5, 2023
ce85cbd
Add missing Returns section to docstring
CPBridge Jul 5, 2023
53a2392
Remove has_ref_frame_uid from _get_dimension_index_values
CPBridge Jul 5, 2023
10340a8
rename index_values -> dimension_index_values
CPBridge Jul 5, 2023
0881c32
Fix missing variable name change
CPBridge Jul 5, 2023
1774199
Add further explanation to _get_dimension_index_values docstring
CPBridge Jul 6, 2023
5cbd9bf
Simplify _omit_empty_frames to find indices only
CPBridge Jul 6, 2023
37ad160
Rewrite as list comprehension
CPBridge Jul 6, 2023
27eac18
remove unnecessary flattens to save memory
CPBridge Jul 7, 2023
26cdf0c
Merge branch 'seg_creation_efficiency' of github.com:ImagingDataCommo…
CPBridge Jul 8, 2023
287047e
Tidy up of dimension indexing code
CPBridge Jul 8, 2023
c0d9f72
Add further docstring comment for _get_segment_array
CPBridge Jul 8, 2023
30bb5f7
Update src/highdicom/seg/sop.py
CPBridge Jul 27, 2023
f7878d5
Apply suggestions from code review
CPBridge Jul 27, 2023
9092a00
Rename methods, fix lints
CPBridge Jul 27, 2023
551c2cd
Merge pull request #227 from ImagingDataCommons/seg_creation_efficiency
CPBridge Jul 27, 2023
378c740
Add multithreading option to segmentation creation
CPBridge Jul 5, 2023
be6963b
Remove array copy, seems to be unnecessary
CPBridge Jul 5, 2023
808af48
Add test
CPBridge Jul 5, 2023
9934170
Add section to docs on multiprocessing
CPBridge Jul 5, 2023
08a82ba
Retrieve one segment array at a time: simpler and slightly faster
CPBridge Jul 5, 2023
47286dd
Fix docstring for _get_segment_array
CPBridge Jul 6, 2023
22e9024
add ability for user to pass their own process pool
CPBridge Jul 8, 2023
2aee263
Add basic tiled_full option
CPBridge Jul 27, 2023
29e45bf
Fixes
CPBridge Jul 27, 2023
c1dfdc6
Test for optional parameters
erikogabrielsson Jun 23, 2023
b6e68c0
Raise ValueError if duplicating "Processing Step Description"
erikogabrielsson Jun 23, 2023
97be91c
Fix concept code name for DateTimeContentItem
erikogabrielsson Jun 23, 2023
afb3d7f
Add additional properties
erikogabrielsson Jun 23, 2023
5407c9f
Tests for datetime-based content items
erikogabrielsson Jun 23, 2023
852414a
Fix for parsing of datetime-type content
erikogabrielsson Jun 23, 2023
4d734fd
Add specimen container and specimen type
erikogabrielsson Jun 23, 2023
7108886
Fix type annotation
erikogabrielsson Jun 24, 2023
0ca5b8f
Return simple datetime
erikogabrielsson Jun 24, 2023
39341a3
Add addtitional parameters to SpecimenDescription
erikogabrielsson Aug 3, 2023
bf5e05c
Fix spelling and duplicate test classes
erikogabrielsson Aug 3, 2023
87fc760
Flake8 fixes
erikogabrielsson Aug 3, 2023
5c5cbb6
Merge variables used for compressed and uncompressed frames
CPBridge Aug 7, 2023
74d5e83
Switch ordering of tiles to actually match TILED_FULL
CPBridge Aug 10, 2023
cde407d
Refactor utils code into iter_tiled_full_frame_data
CPBridge Aug 18, 2023
8f71961
Fix types
CPBridge Aug 18, 2023
7bbdc26
Fix tests relating to order of dimension indices
CPBridge Aug 18, 2023
b44ff78
Basic implementation of autotiling. Need to add omitting frame logic
CPBridge Aug 24, 2023
33f635e
Add frame omission logic and tests
CPBridge Aug 31, 2023
f15aa1a
Work in progress implementation of constructing tiled outputs
CPBridge Sep 2, 2023
6109eed
Working implementation of tiled outputs, need tests
CPBridge Sep 4, 2023
c50614f
Add tests, fix bug with padded tiles
CPBridge Sep 10, 2023
402371a
Add doc sections on total pixel matrices for seg creation and parsing
CPBridge Sep 11, 2023
f1a284b
Add tiled full test image, links in docs
CPBridge Sep 11, 2023
2d2110e
Minor typos in docstrings
CPBridge Sep 12, 2023
f118207
Add pyramid utility method
CPBridge Sep 14, 2023
187539c
Add export of create_segmentation_pyramid
CPBridge Sep 14, 2023
0858800
Fixes to pyramid
CPBridge Sep 14, 2023
e8eb0f5
Fix error reading TILED_FULL segmentations from file
CPBridge Sep 14, 2023
84df578
Merge branch 'feat/autotile' into feat/pyramid
CPBridge Sep 14, 2023
811bb59
Add full docs
CPBridge Sep 14, 2023
9289950
Merge pull request #245 from ImagingDataCommons/seg_multithreading
CPBridge Sep 15, 2023
7259596
Merge branch 'master' into v0.22.0dev
CPBridge Sep 20, 2023
b76bcbf
Merge branch 'v0.22.0dev' into feat/tiled_full
CPBridge Sep 20, 2023
ac77352
Fix variable name change introduced by merge
CPBridge Sep 20, 2023
a7926f3
Merge branch 'feat/tiled_full' into feat/autotile
CPBridge Sep 20, 2023
cf133f2
Merge branch 'feat/autotile' into feat/pyramid
CPBridge Sep 20, 2023
ee598f7
Add docstrings to new properties
erikogabrielsson Sep 24, 2023
979ff9f
Append steps before adding preparation step sequence to dataset
erikogabrielsson Sep 24, 2023
9885683
Move new optional arguments to end of init
erikogabrielsson Sep 24, 2023
1f7fb58
Add reference to valid container type
erikogabrielsson Sep 24, 2023
e15bf57
Merge branch 'SpecimenPreparationStep-fixes' of https://github.com/er…
erikogabrielsson Sep 24, 2023
149aa6f
Add references to CIDs
erikogabrielsson Sep 24, 2023
6d0339c
Check for type of issuer of specimen id on constructed preparation step
erikogabrielsson Sep 24, 2023
6b8d2aa
Test date, time, and datetime conversion from dataset with pydicom co…
erikogabrielsson Sep 24, 2023
921c14b
Add docstrings for missed properties
erikogabrielsson Sep 24, 2023
9ee5879
Fix typo in docstring
erikogabrielsson Sep 24, 2023
f9c40fa
Add from_dataset for IssuerOfIdentifier
erikogabrielsson Sep 24, 2023
345a512
Add description of issuer_of_specimen_id argument
erikogabrielsson Sep 24, 2023
2b269bb
Add 'copy' parameter to from_dataset()-method
erikogabrielsson Sep 25, 2023
187f668
Check that specimen_short_description is valid
erikogabrielsson Sep 25, 2023
e424f43
Tests for specimen_short_description
erikogabrielsson Sep 25, 2023
9d9fb4e
Add reference to CID for specimen type argument
erikogabrielsson Sep 25, 2023
2984da4
Convert Code specimen_type to CodedConcept
erikogabrielsson Sep 25, 2023
35bcb42
Remove extra line
erikogabrielsson Sep 25, 2023
dfab977
Fix merge conflicts from master
CPBridge Oct 2, 2023
795acdf
Flake 8 fixes
CPBridge Oct 2, 2023
21d617d
Merge branch 'v0.22.0dev' into feat/autotile
CPBridge Oct 2, 2023
8f4b279
Merge branch 'feat/autotile' into feat/pyramid
CPBridge Oct 2, 2023
4cf43cc
Add TotalPixelMatrixFocalPlanes
CPBridge Oct 3, 2023
400cee7
Merge branch 'feat/autotile' into feat/pyramid
CPBridge Oct 3, 2023
28e0ca6
WIP adding input checks for pyramid segs
CPBridge Oct 19, 2023
477c098
WIP pyramid finalization
CPBridge Oct 28, 2023
fa58634
Merge branch 'master' into v0.22.0dev
CPBridge Oct 28, 2023
3c657f7
Add tests for all four pyramid input combinations
CPBridge Nov 9, 2023
6b7401d
Add pyramid doc section
CPBridge Nov 9, 2023
80a81f9
Merge pull request #246 from erikogabrielsson/SpecimenPreparationStep…
CPBridge Nov 9, 2023
20c5e71
Merge pull request #248 from ImagingDataCommons/feat/autotile
CPBridge Nov 9, 2023
5b7e67a
Merge pull request #253 from ImagingDataCommons/feat/pyramid
CPBridge Nov 9, 2023
e15aac1
Version bump for release
CPBridge Nov 9, 2023
a14c3d8
Fix codespell errors
CPBridge Nov 9, 2023
d244e73
Fix wrong transfer syntax in test
CPBridge Nov 9, 2023
1cdab80
Merge pull request #270 from ImagingDataCommons/fix_jpegls_encode_test
CPBridge Nov 9, 2023
393c186
doctest fix
CPBridge Nov 9, 2023
b61aa58
Fix for new seg tests when libjpeg not installed
CPBridge Nov 9, 2023
50c97e5
Workaround use of math.prod in python 3.7
CPBridge Nov 9, 2023
edf6198
Flake8 fix
CPBridge Nov 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified data/test_files/seg_image_sm_control.dcm
Binary file not shown.
Binary file added data/test_files/seg_image_sm_dots_tiled_full.dcm
Binary file not shown.
20 changes: 16 additions & 4 deletions docs/ann.rst
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,20 @@ transmitted over network, etc.
Reading Existing Bulk Annotation Objects
----------------------------------------

You can read an existing bulk annotation object using `pydicom` and then convert
to the `highdicom` object like this:
You can read an existing bulk annotation object from file using the
:func:`highdicom.ann.annread()` function:

.. code-block:: python

from pydicom import dcmread
import highdicom as hd

ann = hd.ann.annread('data/test_files/sm_annotations.dcm')

assert isinstance(ann, hd.ann.MicroscopyBulkSimpleAnnotations)

Alternatively you can converting an existing ``pydicom.Dataset`` representing a
bulk annotation object to the `highdicom` object like this:

.. code-block:: python

Expand All @@ -198,7 +210,7 @@ to the `highdicom` object like this:

assert isinstance(ann, hd.ann.MicroscopyBulkSimpleAnnotations)

Note that this example (and the following examples) uses an example file that
Note that these examples (and the following examples) uses an example file that
you can access from the test data in the `highdicom` repository. It was created
using exactly the code in the construction example above.

Expand Down Expand Up @@ -298,7 +310,7 @@ passed the data in to create the annotation with `highdicom`.
import numpy as np

graphic_data = group.get_graphic_data(
coordinate_type=ann.AnnotationCoordinateType,
coordinate_type=ann.annotation_coordinate_type,
)
assert len(graphic_data) == 2 and isinstance(graphic_data[0], np.ndarray)

Expand Down
331 changes: 331 additions & 0 deletions docs/seg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,240 @@ segments.
device_serial_number='1234567890',
)

Constructing SEG Images from a Total Pixel Matrix
-------------------------------------------------

Some digital pathology images are represented as "tiled" images,
in which the full image (known as the "total pixel matrix") is divided up
into smaller rectangular regions in the row and column dimensions and each
region ("tile") is stored as a frame in a multiframe DICOM image.

Segmentations of such images are stored as a tiled image in the same manner.
There are a two options in `highdicom` for doing this. You can either pass each
tile/frame individually stacked as a 1D list down the first dimension of the
``pixel_array`` as we have already seen (with the location of each frame either
matching that of the corresponding frame in the source image or explicitly
specified in the ``plane_positions`` argument), or you can pass the 2D total
pixel matrix of the segmentation and have `highdicom` automatically create the
tiles for you.

To enable this latter option, pass the ``pixel_array`` as a single frame (i.e.
a 2D labelmap array, a 3D labelmap array with a single frame stacked down the
first axis, or a 4D array with a single frame stacked down the first dimension
and any number of segments stacked down the last dimension) and set the
``tile_pixel_array`` argument to ``True``. You can optionally choose the size
(in pixels) of each tile using the ``tile_size`` argument, or, by default, the
tile size of the source image will be used (regardless of whether the
segmentation is represented at the same resolution as the source image).

If you need to specify the plane positions of the image explicitly, you should
pass a single item to the ``plane_positions`` argument giving the location of
the top left corner of the full total pixel matrix. Otherwise, all the usual
options are available to you.

.. code-block:: python

# Use an example slide microscopy image from the highdicom test data
# directory
sm_image = dcmread('data/test_files/sm_image.dcm')

# The source image has multiple frames/tiles, but here we create a mask
# corresponding to the entire total pixel matrix
mask = np.zeros(
(
sm_image.TotalPixelMatrixRows,
sm_image.TotalPixelMatrixColumns
),
dtype=np.uint8,
)
mask[38:43, 5:41] = 1

property_category = hd.sr.CodedConcept("91723000", "SCT", "Anatomical Structure")
property_type = hd.sr.CodedConcept("84640000", "SCT", "Nucleus")
segment_descriptions = [
hd.seg.SegmentDescription(
segment_number=1,
segment_label='Segment #1',
segmented_property_category=property_category,
segmented_property_type=property_type,
algorithm_type=hd.seg.SegmentAlgorithmTypeValues.MANUAL,
),
]

seg = hd.seg.Segmentation(
source_images=[sm_image],
pixel_array=mask,
segmentation_type=hd.seg.SegmentationTypeValues.BINARY,
segment_descriptions=segment_descriptions,
series_instance_uid=hd.UID(),
series_number=1,
sop_instance_uid=hd.UID(),
instance_number=1,
manufacturer='Foo Corp.',
manufacturer_model_name='Slide Segmentation Algorithm',
software_versions='0.0.1',
device_serial_number='1234567890',
tile_pixel_array=True,
)

# The result stores the mask as a set of 10 tiles of the non-empty region of
# the total pixel matrix, each of size (10, 10), matching # the tile size of
# the source image
assert seg.NumberOfFrames == 10
assert seg.pixel_array.shape == (10, 10, 10)

``"TILED_FULL"`` and ``"TILED_SPARSE"``
---------------------------------------

When the segmentation is stored as a tiled image, there are two ways in which
the locations of each frame/tile may be specified in the resulting object.
These are defined by the value of the
`"DimensionOrganizationType"
<https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.17.html#table_C.7.6.17-1>`_
attribute:

- ``"TILED_SPARSE"``: The position of each tile is explicitly defined in the
`"PerFrameFunctionalGroupsSequence"
<https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.16.html#table_C.7.6.16-1>`_
of the object. This requires a potentially very long sequence to store all
the per-frame metadata, but does allow for the omission of empty frames from
the segmentation and other irregular tiling strategies.
- ``"TILED_FULL"``: The position of each tile is implicitly defined using a
predetermined order of the frames. This saves the need to store the pre-frame
metadata but does not allow for the omission of empty frames of the
segmentation and is generally less flexible. It may also be simpler for a
receiving application to process, since the tiles are guaranteed to be
regularly and consistently ordered.

You can control this behavior by specifying the
``dimension_organization_type`` parameter and passing a value of the
:class:`highdicom.DimensionOrganizationTypeValues` enum. The default value is
``"TILED_SPARSE"``. Generally, the ``"TILED_FULL"`` option will be used in
combination with ``tile_pixel_array`` argument.


.. code-block:: python

# Using the same example as above, this time as TILED_FULL
seg = hd.seg.Segmentation(
source_images=[sm_image],
pixel_array=mask,
segmentation_type=hd.seg.SegmentationTypeValues.BINARY,
segment_descriptions=segment_descriptions,
series_instance_uid=hd.UID(),
series_number=1,
sop_instance_uid=hd.UID(),
instance_number=1,
manufacturer='Foo Corp.',
manufacturer_model_name='Slide Segmentation Algorithm',
software_versions='0.0.1',
device_serial_number='1234567890',
tile_pixel_array=True,
omit_empty_frames=False,
dimeension_organization_type=hd.DimensionOrganizationTypeValues.TILED_FULL,
)

# The result stores the mask as a set of 25 tiles of the entire region of
# the total pixel matrix, each of size (10, 10), matching the tile size of
# the source image
assert seg.NumberOfFrames == 25
assert seg.pixel_array.shape == (25, 10, 10)

Multi-resolution Pyramids
-------------------------

Whole slide digital pathology images can often be very large and as such it
is common to represent them as *multi-resolution pyramids* of images, i.e.
to store multiple versions of the same image at different resolutions. This
helps viewers render the image at different zoom levels.

Within DICOM, this can also extend to segmentations derived from whole slide
images. Multiple different SEG images may be stored, each representing the
same segmentation at a different resolution, as different instances within a
DICOM series.

*highdicom* provides the :func:`highdicom.seg.create_segmentation_pyramid`
function to assist with this process. This function handles multiple related
scenarios:

* Constructing a segmentation of a source image pyramid given a
segmentation pixel array of the highest resolution source image.
Highdicom performs the downsampling automatically to match the
resolution of the other source images. For this case, pass multiple
``source_images`` and a single item in ``pixel_arrays``.
* Constructing a segmentation of a source image pyramid given user-provided
segmentation pixel arrays for each level in the source pyramid. For this
case, pass multiple ``source_images`` and a matching number of
``pixel_arrays``.
* Constructing a segmentation of a single source image given multiple
user-provided downsampled segmentation pixel arrays. For this case, pass
a single item in ``source_images``, and multiple items in
``pixel_arrays``).
* Constructing a segmentation of a single source image and a single
segmentation pixel array by downsampling by a given list of
``downsample_factors``. For this case, pass a single item in
``source_images``, a single item in ``pixel_arrays``, and a list of one
or more desired ``downsample_factors``.

Here is a simple of example of specifying a single source image and segmentation
array, and having *highdicom* create a multi-resolution pyramid segmentation
series at user-specified downsample factors.

.. code-block:: python

import highdicom as hd
from pydicom import dcmread
import numpy as np


# Use an example slide microscopy image from the highdicom test data
# directory
sm_image = dcmread('data/test_files/sm_image.dcm')

# The source image has multiple frames/tiles, but here we create a mask
# corresponding to the entire total pixel matrix
mask = np.zeros(
(
sm_image.TotalPixelMatrixRows,
sm_image.TotalPixelMatrixColumns
),
dtype=np.uint8,
)
mask[38:43, 5:41] = 1

property_category = hd.sr.CodedConcept("91723000", "SCT", "Anatomical Structure")
property_type = hd.sr.CodedConcept("84640000", "SCT", "Nucleus")
segment_descriptions = [
hd.seg.SegmentDescription(
segment_number=1,
segment_label='Segment #1',
segmented_property_category=property_category,
segmented_property_type=property_type,
algorithm_type=hd.seg.SegmentAlgorithmTypeValues.MANUAL,
),
]

# This will create a segmentation series of three images: one at the
# original source image resolution (implicit), one at half the size, and
# another at a quarter of the original size.
seg_pyramid = hd.seg.create_segmentation_pyramid(
source_images=[sm_image],
pixel_arrays=[mask],
segmentation_type=hd.seg.SegmentationTypeValues.BINARY,
segment_descriptions=segment_descriptions,
series_instance_uid=hd.UID(),
series_number=1,
manufacturer='Foo Corp.',
manufacturer_model_name='Slide Segmentation Algorithm',
software_versions='0.0.1',
device_serial_number='1234567890',
downsample_factors=[2.0, 4.0]
)

Note that the :func:`highdicom.seg.create_segmentation_pyramid` function always
behaves as if the ``tile_pixel_array`` input is ``True`` within the segmentation
constructor, i.e. it assumes that the input segmentation masks represent total
pixel matrices.

Representation of Fractional SEGs
---------------------------------
Expand Down Expand Up @@ -606,6 +840,21 @@ and 1):
- The clear frame boundaries make retrieving individual frames from
``"FRACTIONAL"`` image files possible.

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

When creating large, multiframe ``"FRACTIONAL"`` segmentations using a
compressed transfer syntax, the time taken to compress the frames can become
large and dominate the time taken to create the segmentation. By default,
frames are compressed in series using the main process, however the ``workers``
parameter allows you to specify a number of additional worker processes that
will be used to compress frames in parallel. Setting ``workers`` to a negative
number uses all available processes on your machine. Note that while this is
likely to result in significantly lower creations times for segmentations with
a very large number of frames, for segmentations with only a few frames the
additional overhead of spawning processes may in fact slow the entire
segmentation creation process down.

Geometry of SEG Images
----------------------

Expand Down Expand Up @@ -1101,6 +1350,88 @@ as stored in the SEG will be returned.
# [0. 0.2509804 0.5019608]


Reconstructing Total Pixel Matrices from Tiled Segmentations
------------------------------------------------------------

For segmentations of digital pathology images that are stored as tiled images,
the :meth:`highdicom.seg.Segmentation.get_pixels_by_source_frame()` method will
return the segmentation mask as a set of frames stacked down the first
dimension of the array. However, for such images, you typically want to work
with the large 2D total pixel matrix that is formed by correctly arranging the
tiles into a 2D array. `highdicom` provides the
:meth:`highdicom.seg.Segmentation.get_total_pixel_matrix()` method for this
purpose.

Called without any parameters, it returns a 3D array containing the full total
pixel matrix. The first two dimensions are the spatial dimensions, and the
third is the segments dimension. Behind the scenes highdicom has stitched
together the required frames stored in the original file for you. Like with the
other methods described above, setting ``combine_segments`` to ``True``
combines all the segments into, in this case, a 2D array.

.. code-block:: python

import highdicom as hd

# Read in the segmentation using highdicom
seg = hd.seg.segread('data/test_files/seg_image_sm_control.dcm')

# Get the full total pixel matrix
mask = seg.get_total_pixel_matrix()

expected_shape = (
seg.TotalPixelMatrixRows,
seg.TotalPixelMatrixColumns,
seg.number_of_segments,
)
assert mask.shape == expected_shape

# Combine the segments into a single array
mask = seg.get_total_pixel_matrix(combine_segments=True)

assert mask.shape == (seg.TotalPixelMatrixRows, seg.TotalPixelMatrixColumns)

Furthermore, you can request a sub-region of the full total pixel matrix by
specifying the start and/or stop indices for the rows and/or columns within the
total pixel matrix. Note that this method follows DICOM 1-based convention for
indexing rows and columns, i.e. the first row and column of the total pixel
matrix are indexed by the number 1 (not 0 as is common within Python). Negative
indices are also supported to index relative to the last row or column, with -1
being the index of the last row or column. Like for standard Python indexing,
the stop indices are specified as one beyond the final row/column in the
returned array. Note that the requested region does not have to start or stop
at the edges of the underlying frames: `highdicom` stitches together only the
relevant parts of the frames to create the requested image for you.

.. code-block:: python

import highdicom as hd

# Read in the segmentation using highdicom
seg = hd.seg.segread('data/test_files/seg_image_sm_control.dcm')

# Get a region of the total pixel matrix
mask = seg.get_total_pixel_matrix(
combine_segments=True,
row_start=20,
row_end=40,
column_start=10,
column_end=20,
)

assert mask.shape == (20, 10)

# A further example using negative indices. Since row_end is not provided,
# the default behavior is to include the last row in the total pixel matrix.
mask = seg.get_total_pixel_matrix(
combine_segments=True,
row_start=21,
column_start=-30,
column_end=-25,
)

assert mask.shape == (30, 5)

Viewing DICOM SEG Images
------------------------

Expand Down
Loading