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 function to correctly format a DS string #57

Closed
wants to merge 3 commits into from
Closed

Conversation

CPBridge
Copy link
Collaborator

According to the standard, attributes with the decimal string (DS) value representation must contain a maximum of 16 characters (see here). Unfortunately pydicom does not enforce this, and it looks like there is no movement on @hackermd's open issue to rectify this.

A quick fix was previously implemented in the PlanePositionSequence, but this was sub-optimal as it truncated (rather than correctly rounding) and did not handle certain cases correctly (e.g. values that python would write in scientific notation when calling the str() method on a float).

Furthermore, there are several other instances around the codebase where this fix was not implemented, meaning that invalid DS could be created. For example in NumContentItem, which is used for recording nearly all numeric values in an SR document. I found that this was causing many SRs I created to fail the checks in the dciodvfy tool.

This PR implements a new function that I believe correctly handles all edge cases, and rounds properly. It also checks for nans/infs, which would previously be silently stored as strings despite being invalid values for DS. Also added tests and docs.

I have tried to find all the places in the codebase where DS elements are created and switch to using the new function, but I'm not 100% sure I found that I found them all.

from numpy import isfinite


def get_ds_string(f: float) -> str:
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't it make sense for this to be in pydicom instead?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@pieper I agree, this would ideally go into pydicom. There is an option issue (see pydicom/pydicom#1264). @CPBridge have you considered creating a PR to update pydicom.valuerep.DS?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@CPBridge given that this has been merged into pydicom, can we remove this module?

Copy link
Collaborator Author

@CPBridge CPBridge Apr 7, 2021

Choose a reason for hiding this comment

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

yes, there's no sense reviewing this PR in its current form

Copy link
Collaborator

@hackermd hackermd left a comment

Choose a reason for hiding this comment

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

Thanks @CPBridge. This looks great. However, I agree with @pieper that this would ideally go into pydicom. Is this an option?

src/highdicom/content.py Outdated Show resolved Hide resolved
from numpy import isfinite


def get_ds_string(f: float) -> str:
Copy link
Collaborator

Choose a reason for hiding this comment

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

@pieper I agree, this would ideally go into pydicom. There is an option issue (see pydicom/pydicom#1264). @CPBridge have you considered creating a PR to update pydicom.valuerep.DS?

@CPBridge
Copy link
Collaborator Author

I would certainly agree with @hackermd and @pieper that this should be fixed at the pydicom level and this was my first thought also, but given this response in the existing issue in pydicom:

"...pydicom should leave it to the calling code to figure out how to truncate the value."

I got the impression that this would not be welcomed.

However I suppose I night be reading too much into this and any code that does not force pydicom users to truncate their numbers in a certain way might still be welcomed. I could try adding it as a utility function in pydicom that users of pydicom can choose to use explicitly.

Let's park this PR here for now and I will submit a version to pydicom and see where that leads.

@hackermd
Copy link
Collaborator

However I suppose I night be reading too much into this and any code that does not force pydicom users to truncate their numbers in a certain way might still be welcomed. I could try adding it as a utility function in pydicom that users of pydicom can choose to use explicitly.

Handling of DS is tricky and can cause really nasty bugs. I understand that we need to workaround compliance issues, but it shouldn't go as far as making it hard for people to actually create valid DICOM in the first place.

An approach as proposed in #56 (comment) may help with that.

@CPBridge
Copy link
Collaborator Author

CPBridge commented Mar 31, 2021

My approach here is to:

  • Get the functionality for correctly truncating a float into a 16 char string into pydicom, but have it entirely optional for users of pydicom (they would need to explicitly call it)
  • Have users of highdicom continue to pass floats into the highdicom API, and then call the truncation routine from pydicom whenever a DS attribute is set within highdicom code (the same places I updated in the original PR)

Sound like a good approach?

@hackermd
Copy link
Collaborator

Sound like a good approach?

Sounds good to me. @darcymason what do you think? Would that be a good compromise?

@CPBridge CPBridge marked this pull request as draft March 31, 2021 02:33
@darcymason
Copy link

Sounds good to me. @darcymason what do you think? Would that be a good compromise?

Given that it is proposed as an explicit call, I can't see any problem with that.

@CPBridge
Copy link
Collaborator Author

CPBridge commented Apr 4, 2021

See pydicom/pydicom#1334

@CPBridge
Copy link
Collaborator Author

CPBridge commented Apr 7, 2021

This functionality has just been merged into pydicom master branch pydicom/pydicom#1334 and should be in the next release of pydicom (looks like this will be 2.2.0). I will adapt this PR to use that function instead

@@ -0,0 +1,36 @@
import pytest
Copy link
Collaborator

Choose a reason for hiding this comment

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

@CPBridge I assume we no longer need these tests in highdicom, do we?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No this PR was originally written with the assumption that the functionality would be in highdicom and I haven't adapted it to at all yet for the fact that the functionality has moved to pydicom. A lot will need to change. I might just create a new PR from scratch, not sure which will end up being easier

@hackermd hackermd self-assigned this Apr 7, 2021
@hackermd hackermd added bug Something isn't working enhancement New feature or request labels Apr 7, 2021
@CPBridge
Copy link
Collaborator Author

CPBridge commented Apr 8, 2021

Closing in favour of #65

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants