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

Add coordinate for Measurement in SR #307

Merged

Conversation

Fedalto
Copy link
Contributor

@Fedalto Fedalto commented Sep 26, 2024

This adds support for row 3 and 4 from TID 320.

Resolves #306

This adds support for row 3 and 4 from TID 320.

Resolves ImagingDataCommons#306
self,
graphic_type: Union[GraphicTypeValues, str],
graphic_data: np.ndarray,
source_image: SourceImageForRegion,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has nothing to do with ImageRegion, but I reused SourceImageForRegion here as it needs to be an image with SELECTED FROM relationship. Not sure if there's a better option.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting question. SourceImageForRegion pretty much matches the requirements of the ImageContentItem that would be used here, but the concept name used may pose a problem.

The concept name is blank in table TID 320, but the highdicom content item class does not allow "unnamed" content items (this issue arises in a few places and we made this design decision fairly early on). SourceImageForRegion uses the concept name (DCM, 111040, Original Source), whereas it states at the bottom of TID 320 that

(260753009, SCT, "Source") may be be used as a generic Concept Name when there is a desire to avoid having an anonymous (unnamed) Content Item

so we should follow that here, which implies that SourceImageForRegion as it is currently written would not be appropriate.

However, looking at the other places that SourceImageForRegion is used (basically row six of TID 1410 and TID 1411), I notice that the same suggestion to use SCT 260753009 is given there. I am not sure why SourceImageForRegion uses DCM 111040, it appears that this goes all the way back to the beginning of the library. Perhaps @hackermd can shed some light

Given this, I favour changing SourceImageForRegion to use SCT 260753009 and then using SourceImageForRegion as you propose for TID 320.

@@ -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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created another option and used the same referenced_images parameter.

Another solution I see would be to expand SourceImageForMeasurement and implement all options from TID 320 in it.

If a SCOORD is given, use row 3 and 4. If SCOORD3D, then make it use row 6 (I have not implemented this). If no coordinate is passed, use row 1 (which is the current SourceImageForMeasurement functionality)

Copy link
Collaborator

@CPBridge CPBridge Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I can see that we are a little backed into a corner here if we want to preserve backward compatibility. However I think passing coordinates to a parameter named referenced_images is rather non-intuitive.

I think I would instead suggest that we add a new parameter named referenced_coordinates, and allow passing either a sequence of CoordinatesForMeasurement (rows 3 and 4 of TID 320) or a sequence of CoordinatesForMeasurement3D (row 6), which has not been implemented yet but would be a relatively straightforward subclass of Scoord3DContentItem with a sensible default concept name.

Then you would need to document and enforce that using referenced_images and referenced_coordinates are mutually exclusive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you would need to document and enforce that using referenced_images and referenced_coordinates are mutually exclusive.

I don't think this is the case.
See row 13 of TID 300. Measurement, it has VM 1-n, so I understand that referenced_images and referenced_coordinates can both be used inside a single Measurement.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yes I guess you are right. They are mutually exclusive within TID 320, but there's no requirement that multiple uses of TID320 within TID300 have the same type

@CPBridge CPBridge changed the base branch from master to v0.24.0dev September 30, 2024 00:52
Copy link
Collaborator

@CPBridge CPBridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Fedalto, thanks for the nice PR!

Generally speaking, it's rather unfortunate that the way this was originally implemented (with only row 1 of TID 320 being used) makes it a little harder to implement this in a backwards-compatible manner.

A couple of other requests, if you are willing:

  • Ideally we would add a referenced_coordinates property to the Measurement class to retrieve the coordinates from the object? This would be very similar to how the referenced_images property works now (though, given some of other comments, I suppose it would be best not to specify a concept name, as in fact we probably should not have done for referenced_images).
  • Unfortunately the Measurement class not only implements TID 300 as it is used within TID 1501 but also rows 5 and after of TID 1491 (which in turn is used within TID 1410 and TID 1411). This was to simplify code and reduce duplication, but unfortunately the two are not exactly the same, and these coordinates are not included in TID 1419. I think this is why this is why these options were not included in the first place. Though technically the template is extensible, I think it would be best for the _ROIMeasurementsAndQualitativeEvaluations class to check any measurements it is passed do not contain SCOORD or SCOORD3D content items, since those templates are supposed to work rather differently (with the region defined as part of the TID 1410 and TID 1411 templates).

Note that I also target your PR at a new v0.24.0 branch rather than master.

self,
graphic_type: Union[GraphicTypeValues, str],
graphic_data: np.ndarray,
source_image: SourceImageForRegion,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting question. SourceImageForRegion pretty much matches the requirements of the ImageContentItem that would be used here, but the concept name used may pose a problem.

The concept name is blank in table TID 320, but the highdicom content item class does not allow "unnamed" content items (this issue arises in a few places and we made this design decision fairly early on). SourceImageForRegion uses the concept name (DCM, 111040, Original Source), whereas it states at the bottom of TID 320 that

(260753009, SCT, "Source") may be be used as a generic Concept Name when there is a desire to avoid having an anonymous (unnamed) Content Item

so we should follow that here, which implies that SourceImageForRegion as it is currently written would not be appropriate.

However, looking at the other places that SourceImageForRegion is used (basically row six of TID 1410 and TID 1411), I notice that the same suggestion to use SCT 260753009 is given there. I am not sure why SourceImageForRegion uses DCM 111040, it appears that this goes all the way back to the beginning of the library. Perhaps @hackermd can shed some light

Given this, I favour changing SourceImageForRegion to use SCT 260753009 and then using SourceImageForRegion as you propose for TID 320.

Comment on lines 722 to 726
name=CodedConcept(
value='121112',
meaning='Source of Measurement',
scheme_designator='DCM'
),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think hard-coding this is too restrictive. The template allows any purpose, and the note for row 13 in table TID 300 explicitly suggests an alternative ("Arm of Angle" in that case, which is clearly not general).

I would suggest that we add a purpose parameter, accepting a CodedConcept or pydicom.sr.coding.Code. I would probably make sense to have "Source of Measurement" as a default value to simplify things for users. (I know SourceImageForMeasurement does hard code the purpose, but I can see more cases where you would want to convey a more fine-grained purpose for coordinates).

src/highdicom/sr/templates.py Outdated Show resolved Hide resolved
@@ -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,
Copy link
Collaborator

@CPBridge CPBridge Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I can see that we are a little backed into a corner here if we want to preserve backward compatibility. However I think passing coordinates to a parameter named referenced_images is rather non-intuitive.

I think I would instead suggest that we add a new parameter named referenced_coordinates, and allow passing either a sequence of CoordinatesForMeasurement (rows 3 and 4 of TID 320) or a sequence of CoordinatesForMeasurement3D (row 6), which has not been implemented yet but would be a relatively straightforward subclass of Scoord3DContentItem with a sensible default concept name.

Then you would need to document and enforce that using referenced_images and referenced_coordinates are mutually exclusive.

Fedalto and others added 4 commits October 3, 2024 06:58
Co-authored-by: Chris Bridge <[email protected]>
Extract the `CoordinatesForMeasurement` in its own parameter.

Add `CoordinatesForMeasurement.from_dataset`.
This follows the recommendation in the spec
@CPBridge
Copy link
Collaborator

CPBridge commented Oct 3, 2024

@Fedalto is this ready for another review?

@Fedalto
Copy link
Contributor Author

Fedalto commented Oct 4, 2024

Most of it.

I just realized that TID 1411 also does not use TID 300, which I thought it did. So the check I did in 1410 (Planar ROI Measurements and Qualitative Evaluations) needs indeed to be in _ROIMeasurementsAndQualitativeEvaluations as you had suggested.
I'll make this change today and will let you know

…Evaluations`

TID 1410 (Planar ROI Measurements and Qualitative Evaluations) and TID
1411 (Volumetric ROI Measurements and Qualitative Evaluations) make use
of TID 1419 (ROI Measurements) which does not include coordinates like
TID 300 (Measurements).
@Fedalto Fedalto force-pushed the add-coordinates-measurements-sr branch from 3580c65 to 9f6d673 Compare October 4, 2024 12:13
@Fedalto
Copy link
Contributor Author

Fedalto commented Oct 4, 2024

Hi @CPBridge. This should be ready for another review. I tried to separate each change request in each commit, so it should be easier to review commit by commit.

Thanks.

Copy link
Collaborator

@CPBridge CPBridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Fedalto, thanks for your work on this. There are a few comments above.

Also, if we are adding referenced coordinates I would prefer to add the SCOORD3D case at the same time (though I'm not concerned about the "R-INFERRED" variants, which honestly confuse me). Would you be happy to add this?

I think it would be pretty straightforward since it does not need the nested source image content item. We could just pass a plain Scoord3DContentItem into the Measurement init method, i.e. make referenced_coordinates of type Optional[Sequence[Union[CoordinatesForMeasurement, Scoord3DContentItem]]], and also have the corresponding property search for and return items of value type SCOORD or SCOORD3D.

But since this would force the user to pick a concept name/purpose and I think having sane defaults is extremely important in this highly complex SR stuff, I would actually lean towards defining a new CoordinatesForMeasurement3D class, which is basically just a Scoord3DContentItem derived class with a little default value for the purpose (just like the existing CoordinatesForMeasurement class. I'm also happy to work on this if you prefer.

Comment on lines 716 to 717
) -> None:
graphic_type = GraphicTypeValues(graphic_type)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a docstring for the __init__ method?

src/highdicom/sr/content.py Outdated Show resolved Hide resolved
tracking_identifier=self._tracking_identifier,
referenced_region=self._region,
measurements=[measurement],
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a test somewhere that the new referenced_coordinates property works as expected, and returns the right number of items and correct type?

@@ -2431,6 +2433,8 @@ def __init__(
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_coordinates: Union[Sequence[highdicom.sr.CoordinatesForMeasurement], None], optional
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh one more thing, can we add a quick note to say that this is not valid for measurements that will later be included in Planar/Volumetric ROI Measurement groups?

@Fedalto
Copy link
Contributor Author

Fedalto commented Oct 15, 2024

Hi @CPBridge.
I made the requested changes. I believe this is ready for another review.
Thanks

Copy link
Collaborator

@CPBridge CPBridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Fedalto, a nice contribution!

I have tweaked things a bit to use the CoordinatesForMeasurement3D class instead of the Scoord3DContentItem as this will be less confusing for users I think. However a Scoord3DContentItem should still work

src/highdicom/sr/templates.py Show resolved Hide resolved
src/highdicom/sr/templates.py Outdated Show resolved Hide resolved
src/highdicom/sr/templates.py Outdated Show resolved Hide resolved
src/highdicom/sr/templates.py Outdated Show resolved Hide resolved
src/highdicom/sr/templates.py Outdated Show resolved Hide resolved
src/highdicom/sr/templates.py Outdated Show resolved Hide resolved
tests/test_sr.py Show resolved Hide resolved
@CPBridge CPBridge merged commit ded30b1 into ImagingDataCommons:v0.24.0dev Oct 19, 2024
@Fedalto Fedalto deleted the add-coordinates-measurements-sr branch October 24, 2024 11:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add SCOORD to a Measurement in SR (TID 320)
2 participants