Skip to content

Commit

Permalink
Add coordinate for Measurement in SR
Browse files Browse the repository at this point in the history
This adds support for row 3 and 4 from TID 320.

Resolves #306
  • Loading branch information
Fedalto committed Sep 26, 2024
1 parent cb62737 commit 65eaf3b
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/highdicom/sr/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Package for creation of Structured Report (SR) instances."""
from highdicom.sr.coding import CodedConcept
from highdicom.sr.content import (
CoordinatesForMeasurement,
FindingSite,
ImageRegion,
ImageRegion3D,
Expand Down Expand Up @@ -104,6 +105,7 @@
'ContentItem',
'ContentSequence',
'ContentSequence',
'CoordinatesForMeasurement',
'DateContentItem',
'DateTimeContentItem',
'DeviceObserverIdentifyingAttributes',
Expand Down
24 changes: 24 additions & 0 deletions src/highdicom/sr/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,30 @@ def from_dataset(
return cast(SourceSeriesForSegmentation, item)


class CoordinatesForMeasurement(ScoordContentItem):

"""Content item representing spatial coordinates of a measurement"""

def __init__(
self,
graphic_type: Union[GraphicTypeValues, str],
graphic_data: np.ndarray,
source_image: SourceImageForRegion,
) -> None:
graphic_type = GraphicTypeValues(graphic_type)
super().__init__(
name=CodedConcept(
value='121112',
meaning='Source of Measurement',
scheme_designator='DCM'
),
graphic_type=graphic_type,
graphic_data=graphic_data,
relationship_type=RelationshipTypeValues.INFERRED_FROM,
)
self.ContentSequence = [source_image]


class ImageRegion(ScoordContentItem):

"""Content item representing an image region of interest in the
Expand Down
12 changes: 6 additions & 6 deletions src/highdicom/sr/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from highdicom.sr.coding import CodedConcept
from highdicom.sr.content import (
FindingSite,
CoordinatesForMeasurement, FindingSite,
LongitudinalTemporalOffsetFromEvent,
ImageRegion,
ImageRegion3D,
Expand Down Expand Up @@ -2386,7 +2386,7 @@ def __init__(
finding_sites: Optional[Sequence[FindingSite]] = None,
method: Optional[Union[CodedConcept, Code]] = None,
properties: Optional[MeasurementProperties] = None,
referenced_images: Optional[Sequence[SourceImageForMeasurement]] = None,
referenced_images: Optional[Sequence[Union[SourceImageForMeasurement, CoordinatesForMeasurement]]] = None,
referenced_real_world_value_map: Optional[RealWorldValueMap] = None
):
"""
Expand Down Expand Up @@ -2429,8 +2429,8 @@ def __init__(
Measurement properties, including evaluations of its normality
and/or significance, its relationship to a reference population,
and an indication of its selection from a set of measurements
referenced_images: Union[Sequence[highdicom.sr.SourceImageForMeasurement], None], optional
Referenced images which were used as sources for the measurement
referenced_images: Union[Sequence[Union[highdicom.sr.SourceImageForMeasurement, highdicom.sr.CoordinatesForMeasurement]], None], optional
Referenced images or coordinates which were used as sources for the measurement
referenced_real_world_value_map: Union[highdicom.sr.RealWorldValueMap, None], optional
Referenced real world value map for referenced source images
Expand Down Expand Up @@ -2495,10 +2495,10 @@ def __init__(
content.extend(properties)
if referenced_images is not None:
for image in referenced_images:
if not isinstance(image, SourceImageForMeasurement):
if not isinstance(image, (SourceImageForMeasurement, CoordinatesForMeasurement)):
raise TypeError(
'Arguments "referenced_images" must have type '
'SourceImageForMeasurement.'
'SourceImageForMeasurement or CoordinatesForMeasurement.'
)
content.append(image)
if referenced_real_world_value_map is not None:
Expand Down
25 changes: 25 additions & 0 deletions tests/test_sr.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
VolumetricROIMeasurementsAndQualitativeEvaluations,
srread,
)
from highdicom.sr.content import CoordinatesForMeasurement
from highdicom.sr.utils import find_content_items
from highdicom import UID

Expand Down Expand Up @@ -2141,6 +2142,30 @@ def test_from_invalid_source_image_seg(self):
)


class TestCoordinatesForMeasurement(unittest.TestCase):

def setUp(self):
super().setUp()
self.source_image = SourceImageForRegion(
referenced_sop_class_uid='1.2.840.10008.5.1.4.1.1.2',
referenced_sop_instance_uid='1.2.3.4.5.6.7.8.9.10'
)

def test_construction(self):
item = CoordinatesForMeasurement(
graphic_type=GraphicTypeValues.MULTIPOINT,
graphic_data=np.array([[10, 20], [20, 30]]),
source_image=self.source_image
)

assert item.graphic_type == GraphicTypeValues.MULTIPOINT
assert item.GraphicData == [10, 20, 20, 30]
assert item.relationship_type == RelationshipTypeValues.INFERRED_FROM

assert len(item.ContentSequence) == 1
assert item.ContentSequence[0] == self.source_image
assert item.ContentSequence[0].relationship_type == RelationshipTypeValues.SELECTED_FROM

class TestReferencedSegment(unittest.TestCase):

def setUp(self):
Expand Down

0 comments on commit 65eaf3b

Please sign in to comment.