Skip to content

Allow parsing of NUM content items without a measured value - #476

Merged
CPBridge merged 3 commits into
ImagingDataCommons:masterfrom
DG47:bugfix/num-empty-measured-value
Oct 22, 2026
Merged

CPBridge merged 3 commits into
ImagingDataCommons:masterfrom
DG47:bugfix/num-empty-measured-value

Conversation

@DG47

@DG47 DG47 commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Fixes #475

The Measured Value Sequence (0040,A300) of a NUM content item is type 2 and may be empty (PS3.3 Table C.18.1-1, "Zero or one Item shall be included in this Sequence"). In that case the Numeric Value Qualifier Code Sequence (0040,A301) is required and gives the reason for the absence of the value, for example "Measurement failure" or "Not a number" from CID 42. highdicom raised an IndexError when decoding such an item, so srread() and Comprehensive3DSR.from_dataset() (and the other SR from_dataset() methods) failed for any document that contained one.

Changes:

  • src/highdicom/sr/value_types.py: NumContentItem.from_dataset() only converts the units code when the Measured Value Sequence has an item. NumContentItem.value and NumContentItem.unit return None when the sequence is empty.
  • src/highdicom/sr/templates.py: Measurement.from_sequence() rebuilt the measurement by passing item.value and item.unit to the constructor, which requires a numeric value. It now copies the content item, the same way _MeasurementsAndQualitativeEvaluations.from_sequence() does, so get_measurements() works for measurements without a value. Measurement.value and Measurement.unit return None in that case.

Construction is unchanged: NumContentItem and Measurement still require a value and a unit. For items that have a measured value, the parsed values are the same as before.

Tests (tests/test_sr.py):

  • NumContentItem.from_dataset() with an empty Measured Value Sequence and a qualifier
  • Measurement.from_sequence() with and without a measured value
  • reading a Comprehensive 3D SR whose TID 1500 measurement has no value, then get_planar_roi_measurement_groups() and get_measurements()

Three of the new tests fail on master. The one that passes on master covers Measurement.from_sequence() for a normal measurement and guards the existing behaviour. All new tests pass with this change.

Validation:

  • pytest tests/test_sr.py -k "without_measured_value or without_value or TestMeasurement and from_sequence": 5 passed (3 failed on master)
  • pytest tests: 2042 passed, 153 skipped
  • flake8 --exclude='bin,build,.eggs,.venv': clean

The Measured Value Sequence (0040,A300) of a NUM content item is type 2
and may be empty, in which case the Numeric Value Qualifier Code
Sequence (0040,A301) gives the reason for the absence of the value
(e.g. "Measurement failure"). Parsing such an item raised an IndexError,
which made srread() and the SR from_dataset() methods fail for any
document containing one.

NumContentItem.from_dataset() now accepts an empty Measured Value
Sequence, and the value and unit properties of NumContentItem and
Measurement return None in that case. Measurement.from_sequence() now
copies the content item instead of passing its value to the
constructor, so that measurements without a value can be retrieved via
get_measurements().
@CPBridge CPBridge added the bug Something isn't working label Oct 22, 2026

@CPBridge CPBridge left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @DG47, a couple of minor suggestions but overall looks good. I appreciate the help

Comment thread src/highdicom/sr/templates.py Resolved
Comment thread tests/test_sr.py Outdated
Comment on lines +2937 to +2940
dataset = Dataset(measurement[0])
dataset.MeasuredValueSequence = []
item = NumContentItem.from_dataset(dataset)
retrieved = Measurement.from_sequence([item])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wouldn't this be a more natural and appropriate test?

Suggested change
dataset = Dataset(measurement[0])
dataset.MeasuredValueSequence = []
item = NumContentItem.from_dataset(dataset)
retrieved = Measurement.from_sequence([item])
measurement[0].MeasuredValueSequence = []
retrieved = Measurement.from_sequence(measurement)

Maybe I am missing something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, you're right, that's simpler and it works. Changed in 879b754. The test still fails on master.

@DG47
DG47 requested a review from CPBridge October 22, 2026 18:35
@CPBridge

Copy link
Copy Markdown
Collaborator

Looks good, thanks @DG47

@CPBridge
CPBridge merged commit 0f6034f into ImagingDataCommons:master Oct 22, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SR documents with a NUM content item that has an empty Measured Value Sequence cannot be read

2 participants