Skip to content

Commit

Permalink
Full testing of seg parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
CPBridge committed Jun 27, 2021
1 parent 7fe1b3d commit 77ea145
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 3 deletions.
29 changes: 29 additions & 0 deletions data/test_files/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Test Files

These files are used for automated tests of the highdicom library. Note that
many more test files are available as part of the pydicom package.

### Images
* `ct_image.dcm` - A small CT image.
* `sm_image.dcm` - A slide microscopy image.

### Segmentation Images
* `seg_image_ct_binary.dcm` - Segmentation image of `BINARY` type derived
from the series of small CT images in the pydicom test files
(`dicomdirtests/77654033/CT2/*`) using the highdicom library.
* `seg_image_ct_binary_overlap.dcm` - Segmentation image of `BINARY` type derived
from the series of small CT images in the pydicom test files
(`dicomdirtests/77654033/CT2/*`) using the highdicom library. This example
contains 2 overlapping segments.
* `seg_image_ct_binary_fractional.dcm` - Segmentation image of `FRACTIONAL`
type but with binary values (i.e. only 0.0 and 1.0) derived from the series
of small CT images in the pydicom test files (`dicomdirtests/77654033/CT2/*`)
using the highdicom library.
* `seg_image_ct_true_fractional.dcm` - Segmentation image of `FRACTIONAL`
type with true fractional values derived from the series
of small CT images in the pydicom test files (`dicomdirtests/77654033/CT2/*`)
using the highdicom library.

### Structured Report Documents
* `sr_document.dcm` - A simple SR document following TID 1500 describing
measurements of a CT image.
Binary file added data/test_files/seg_image_ct_binary.dcm
Binary file not shown.
Binary file not shown.
Binary file added data/test_files/seg_image_ct_binary_overlap.dcm
Binary file not shown.
Binary file added data/test_files/seg_image_ct_true_fractional.dcm
Binary file not shown.
62 changes: 59 additions & 3 deletions src/highdicom/seg/sop.py
Original file line number Diff line number Diff line change
Expand Up @@ -1517,6 +1517,7 @@ def _get_pixels_by_seg_frame(
segment_numbers: np.ndarray,
combine_segments: bool = False,
relabel: bool = False,
rescale_fractional: bool = True
) -> np.ndarray:
"""Construct a segmentation array given an array of frame numbers.
Expand Down Expand Up @@ -1548,6 +1549,13 @@ def _get_pixels_by_seg_frame(
``len(segment_numbers)`` (inclusive) accoring to the position of
the original segment numbers in ``segment_numbers`` parameter. If
``combine_segments`` is ``False``, this has no effect.
rescale_fractional: bool
If this is a FRACTIONAL segmentation and ``rescale_fractional`` is
True, the raw integer-valued array stored in the segmentation image
output will be rescaled by the MaximumFractionalValue such that
each pixel lies in the range 0.0 to 1.0. If False, the raw integer
values are returned. If the segmentation has BINARY type, this
parameter has no effect.
Returns
-------
Expand Down Expand Up @@ -1614,10 +1622,26 @@ def _get_pixels_by_seg_frame(
pixel_array[out_frm, :, :, out_seg] = \
self.pixel_array[seg_frame_ind, :, :]

if rescale_fractional:
if self.segmentation_type == SegmentationTypeValues.FRACTIONAL:
if pixel_array.max() > self.MaximumFractionalValue:
raise RuntimeError(
'Segmentation image contains values greater than the '
'MaximumFractionalValue recorded in the dataset.'
)
max_val = self.MaximumFractionalValue
pixel_array = pixel_array.astype(np.float32) / max_val

if combine_segments:
# Check whether segmentation is binary, or fractional with only
# binary values
if self.segmentation_type != SegmentationTypeValues.BINARY:
if not rescale_fractional:
raise ValueError(
'In order to combine segments of a FRACTIONAL '
'segmentation image, rescale_fractional must be '
'set to True.'
)
is_binary = np.isin(
np.unique(pixel_array),
np.array([0.0, 1.0]),
Expand Down Expand Up @@ -1793,7 +1817,8 @@ def get_pixels_by_source_instance(
combine_segments: bool = False,
relabel: bool = False,
ignore_spatial_locations: bool = False,
assert_missing_frames_are_empty: bool = False
assert_missing_frames_are_empty: bool = False,
rescale_fractional: bool = True
) -> np.ndarray:
"""Get a pixel array for a list of source instances.
Expand Down Expand Up @@ -1880,6 +1905,13 @@ def get_pixels_by_source_instance(
source frames are not referenced in the source image. To override
this behavior and return a segmentation frame of all zeros for such
frames, set this parameter to True.
rescale_fractional: bool
If this is a FRACTIONAL segmentation and ``rescale_fractional`` is
True, the raw integer-valued array stored in the segmentation image
output will be rescaled by the MaximumFractionalValue such that
each pixel lies in the range 0.0 to 1.0. If False, the raw integer
values are returned. If the segmentation has BINARY type, this
parameter has no effect.
Returns
-------
Expand All @@ -1898,6 +1930,11 @@ def get_pixels_by_source_instance(
raise ValueError(
'Segment numbers may not be empty.'
)
if isinstance(source_sop_instance_uids, str):
raise TypeError(
'source_sop_instance_uids should be a sequence of UIDs, not a '
'single UID'
)
if len(source_sop_instance_uids) == 0:
raise ValueError(
'Source SOP instance UIDs may not be empty.'
Expand Down Expand Up @@ -1963,6 +2000,7 @@ def get_pixels_by_source_instance(
segment_numbers=np.array(segment_numbers),
combine_segments=combine_segments,
relabel=relabel,
rescale_fractional=rescale_fractional
)

def get_pixels_by_source_frame(
Expand All @@ -1973,7 +2011,8 @@ def get_pixels_by_source_frame(
combine_segments: bool = False,
relabel: bool = False,
ignore_spatial_locations: bool = False,
assert_missing_frames_are_empty: bool = False
assert_missing_frames_are_empty: bool = False,
rescale_fractional: bool = True
):
"""Get a pixel array for a list of frames within a source instance.
Expand Down Expand Up @@ -2065,6 +2104,13 @@ def get_pixels_by_source_frame(
source frames are not referenced in the source image. To override
this behavior and return a segmentation frame of all zeros for such
frames, set this parameter to True.
rescale_fractional: bool
If this is a FRACTIONAL segmentation and ``rescale_fractional`` is
True, the raw integer-valued array stored in the segmentation image
output will be rescaled by the MaximumFractionalValue such that
each pixel lies in the range 0.0 to 1.0. If False, the raw integer
values are returned. If the segmentation has BINARY type, this
parameter has no effect.
Returns
-------
Expand Down Expand Up @@ -2154,6 +2200,7 @@ def get_pixels_by_source_frame(
segment_numbers=np.array(segment_numbers),
combine_segments=combine_segments,
relabel=relabel,
rescale_fractional=rescale_fractional
)

def get_pixels_by_dimension_index_values(
Expand All @@ -2163,7 +2210,8 @@ def get_pixels_by_dimension_index_values(
segment_numbers: Optional[Sequence[int]] = None,
combine_segments: bool = False,
relabel: bool = False,
assert_missing_frames_are_empty: bool = False
assert_missing_frames_are_empty: bool = False,
rescale_fractional: bool = True
):
"""Get a pixel array for a list of dimension index values.
Expand Down Expand Up @@ -2253,6 +2301,13 @@ def get_pixels_by_dimension_index_values(
source frames are not referenced in the source image. To override
this behavior and return a segmentation frame of all zeros for such
frames, set this parameter to True.
rescale_fractional: bool
If this is a FRACTIONAL segmentation and ``rescale_fractional`` is
True, the raw integer-valued array stored in the segmentation image
output will be rescaled by the MaximumFractionalValue such that
each pixel lies in the range 0.0 to 1.0. If False, the raw integer
values are returned. If the segmentation has BINARY type, this
parameter has no effect.
Returns
-------
Expand Down Expand Up @@ -2393,4 +2448,5 @@ def get_pixels_by_dimension_index_values(
segment_numbers=np.array(segment_numbers),
combine_segments=combine_segments,
relabel=relabel,
rescale_fractional=rescale_fractional
)
Loading

0 comments on commit 77ea145

Please sign in to comment.