1717 RealWorldValueMap ,
1818 ReferencedSegment ,
1919 ReferencedSegmentationFrame ,
20+ SourceImageForMeasurementGroup ,
2021 SourceImageForMeasurement ,
2122 SourceImageForSegmentation ,
2223 SourceSeriesForSegmentation
4243)
4344
4445
46+ # Codes missing from pydicom
4547DEFAULT_LANGUAGE = CodedConcept (
4648 value = 'en-US' ,
4749 scheme_designator = 'RFC5646' ,
4850 meaning = 'English (United States)'
4951)
5052_REGION_IN_SPACE = Code ('130488' , 'DCM' , 'Region in Space' )
53+ _SOURCE = CodedConcept (
54+ value = '260753009' ,
55+ scheme_designator = 'SCT' ,
56+ meaning = 'Source' ,
57+ )
5158
5259
5360logger = logging .getLogger (__name__ )
@@ -2417,10 +2424,10 @@ def finding_sites(self) -> List[FindingSite]:
24172424 return []
24182425
24192426
2420- class MeasurementsAndQualitativeEvaluations (Template ):
2427+ class _MeasurementsAndQualitativeEvaluations (Template ):
24212428
2422- """:dcm:`TID 1501 <part16/chapter_A.html#sect_TID_1501>`
2423- Measurement and Qualitative Evaluation Group """
2429+ """Abstract base class for Measurements and Qualitative Evaluation
2430+ templates. """
24242431
24252432 def __init__ (
24262433 self ,
@@ -2436,7 +2443,7 @@ def __init__(
24362443 qualitative_evaluations : Optional [
24372444 Sequence [QualitativeEvaluation ]
24382445 ] = None ,
2439- finding_category : Optional [Union [CodedConcept , Code ]] = None
2446+ finding_category : Optional [Union [CodedConcept , Code ]] = None ,
24402447 ):
24412448 """
24422449
@@ -2597,7 +2604,7 @@ def from_sequence(
25972604 cls ,
25982605 sequence : Sequence [Dataset ],
25992606 is_root : bool = False
2600- ) -> 'MeasurementsAndQualitativeEvaluations ' :
2607+ ) -> '_MeasurementsAndQualitativeEvaluations ' :
26012608 """Construct object from a sequence of datasets.
26022609
26032610 Parameters
@@ -2612,7 +2619,7 @@ def from_sequence(
26122619
26132620 Returns
26142621 -------
2615- highdicom.sr.MeasurementsAndQualitativeEvaluations
2622+ highdicom.sr._MeasurementsAndQualitativeEvaluations
26162623 Content Sequence containing root CONTAINER SR Content Item
26172624
26182625 """
@@ -2632,8 +2639,8 @@ def from_sequence(
26322639 'because it does not have name "Measurement Group".'
26332640 )
26342641 instance = ContentSequence .from_sequence (sequence )
2635- instance .__class__ = MeasurementsAndQualitativeEvaluations
2636- return cast (MeasurementsAndQualitativeEvaluations , instance )
2642+ instance .__class__ = cls
2643+ return cast (cls , instance )
26372644
26382645 @property
26392646 def method (self ) -> Union [CodedConcept , None ]:
@@ -2812,8 +2819,112 @@ def get_qualitative_evaluations(
28122819 ]
28132820
28142821
2822+ class MeasurementsAndQualitativeEvaluations (
2823+ _MeasurementsAndQualitativeEvaluations
2824+ ):
2825+
2826+ """:dcm:`TID 1501 <part16/chapter_A.html#sect_TID_1501>`
2827+ Measurement and Qualitative Evaluation Group"""
2828+
2829+ def __init__ (
2830+ self ,
2831+ tracking_identifier : TrackingIdentifier ,
2832+ referenced_real_world_value_map : Optional [RealWorldValueMap ] = None ,
2833+ time_point_context : Optional [TimePointContext ] = None ,
2834+ finding_type : Optional [Union [CodedConcept , Code ]] = None ,
2835+ method : Optional [Union [CodedConcept , Code ]] = None ,
2836+ algorithm_id : Optional [AlgorithmIdentification ] = None ,
2837+ finding_sites : Optional [Sequence [FindingSite ]] = None ,
2838+ session : Optional [str ] = None ,
2839+ measurements : Sequence [Measurement ] = None ,
2840+ qualitative_evaluations : Optional [
2841+ Sequence [QualitativeEvaluation ]
2842+ ] = None ,
2843+ finding_category : Optional [Union [CodedConcept , Code ]] = None ,
2844+ source_images : Optional [
2845+ Sequence [SourceImageForMeasurementGroup ]
2846+ ] = None ,
2847+ ):
2848+ """
2849+
2850+ Parameters
2851+ ----------
2852+ tracking_identifier: highdicom.sr.TrackingIdentifier
2853+ Identifier for tracking measurements
2854+ referenced_real_world_value_map: Union[highdicom.sr.RealWorldValueMap, None], optional
2855+ Referenced real world value map for region of interest
2856+ time_point_context: Union[highdicom.sr.TimePointContext, None], optional
2857+ Description of the time point context
2858+ finding_type: Union[highdicom.sr.CodedConcept, pydicom.sr.coding.Code, None], optional
2859+ Type of observed finding
2860+ method: Union[highdicom.sr.CodedConcept, pydicom.sr.coding.Code, None], optional
2861+ Coded measurement method (see
2862+ :dcm:`CID 6147 <part16/sect_CID_6147.html>`
2863+ "Response Criteria" for options)
2864+ algorithm_id: Union[highdicom.sr.AlgorithmIdentification, None], optional
2865+ Identification of algorithm used for making measurements
2866+ finding_sites: Sequence[highdicom.sr.FindingSite, None], optional
2867+ Coded description of one or more anatomic locations at which
2868+ finding was observed
2869+ session: Union[str, None], optional
2870+ Description of the session
2871+ measurements: Union[Sequence[highdicom.sr.Measurement], None], optional
2872+ Numeric measurements
2873+ qualitative_evaluations: Union[Sequence[highdicom.sr.QualitativeEvaluation], None], optional
2874+ Coded name-value pairs that describe qualitative evaluations
2875+ finding_category: Union[highdicom.sr.CodedConcept, pydicom.sr.coding.Code, None], optional
2876+ Category of observed finding, e.g., anatomic structure or
2877+ morphologically abnormal structure
2878+ source_images: Optional[Sequence[highdicom.sr.SourceImageForMeasurementGroup]], optional
2879+ Images to that were the source of the measurements. If not provided,
2880+ all images that listed in the document tree of the containing SR
2881+ document are assumed to be source images.
2882+
2883+ """ # noqa: E501
2884+ super ().__init__ (
2885+ tracking_identifier = tracking_identifier ,
2886+ referenced_real_world_value_map = referenced_real_world_value_map ,
2887+ time_point_context = time_point_context ,
2888+ finding_type = finding_type ,
2889+ method = method ,
2890+ algorithm_id = algorithm_id ,
2891+ finding_sites = finding_sites ,
2892+ session = session ,
2893+ measurements = measurements ,
2894+ qualitative_evaluations = qualitative_evaluations ,
2895+ finding_category = finding_category ,
2896+ )
2897+ group_item = self [0 ]
2898+
2899+ if source_images is not None :
2900+ for img in source_images :
2901+ if not isinstance (img , SourceImageForMeasurementGroup ):
2902+ raise TypeError (
2903+ 'Items of argument "source_images" must be of type '
2904+ 'highdicom.sr.SourceImageForMeasurementGroup.'
2905+ )
2906+ group_item .ContentSequence .extend (source_images )
2907+
2908+ @property
2909+ def source_images (self ) -> List [SourceImageForMeasurementGroup ]:
2910+ """List[highdicom.sr.SourceImageForMeasurementGroup]: source images"""
2911+ root_item = self [0 ]
2912+ matches = find_content_items (
2913+ root_item ,
2914+ name = _SOURCE ,
2915+ value_type = ValueTypeValues .IMAGE ,
2916+ relationship_type = RelationshipTypeValues .CONTAINS
2917+ )
2918+ if len (matches ) > 0 :
2919+ return [
2920+ SourceImageForMeasurementGroup .from_dataset (m ) for m in matches
2921+ ]
2922+ return []
2923+
2924+
28152925class _ROIMeasurementsAndQualitativeEvaluations (
2816- MeasurementsAndQualitativeEvaluations ):
2926+ _MeasurementsAndQualitativeEvaluations
2927+ ):
28172928
28182929 """Abstract base class for ROI Measurements and Qualitative Evaluation
28192930 templates."""
@@ -4423,6 +4534,8 @@ def get_image_measurement_groups(
44234534 tracking_uid : Optional [str ] = None ,
44244535 finding_type : Optional [Union [CodedConcept , Code ]] = None ,
44254536 finding_site : Optional [Union [CodedConcept , Code ]] = None ,
4537+ referenced_sop_instance_uid : Optional [str ] = None ,
4538+ referenced_sop_class_uid : Optional [str ] = None
44264539 ) -> List [MeasurementsAndQualitativeEvaluations ]:
44274540 """Get imaging measurements of images.
44284541
@@ -4438,6 +4551,10 @@ def get_image_measurement_groups(
44384551 Finding
44394552 finding_site: Union[highdicom.sr.CodedConcept, pydicom.sr.coding.Code, None], optional
44404553 Finding site
4554+ referenced_sop_instance_uid: Union[str, None], optional
4555+ SOP Instance UID of the referenced instance.
4556+ referenced_sop_class_uid: Union[str, None], optional
4557+ SOP Class UID of the referenced instance.
44414558
44424559 Returns
44434560 -------
@@ -4483,6 +4600,19 @@ def get_image_measurement_groups(
44834600 )
44844601 matches .append (matches_tracking_uid )
44854602
4603+ if (
4604+ (referenced_sop_instance_uid is not None ) or
4605+ (referenced_sop_class_uid is not None )
4606+ ):
4607+ matches_uids = _contains_image_items (
4608+ group_item ,
4609+ name = _SOURCE ,
4610+ referenced_sop_class_uid = referenced_sop_class_uid ,
4611+ referenced_sop_instance_uid = referenced_sop_instance_uid ,
4612+ relationship_type = RelationshipTypeValues .CONTAINS
4613+ )
4614+ matches .append (matches_uids )
4615+
44864616 seq = MeasurementsAndQualitativeEvaluations .from_sequence (
44874617 [group_item ]
44884618 )
0 commit comments