Skip to content

[pixeldata] Fix rendering for files with a window width smaller than 1 - #826

Open
abustany wants to merge 1 commit into
Enet4:masterfrom
abustany:sub-unit-window
Open

abustany wants to merge 1 commit into
Enet4:masterfrom
abustany:sub-unit-window

Conversation

@abustany

Copy link
Copy Markdown
Contributor

On a file with the following tags:

(0028,1050) DS [0.4565323565]                           #  12, 1 WindowCenter
(0028,1051) DS [0.9130647131]                           #  12, 1 WindowWidth
(0028,1052) DS [-0.0002442002587]                       #  16, 1 RescaleIntercept
(0028,1053) DS [0.0002442002587]                        #  16, 1 RescaleSlope

Before:
out

After:
out2

@Enet4 Enet4 added bug This is a bug A-lib Area: library C-pixeldata Crate: dicom-pixeldata labels Aug 27, 2026
@Enet4

Enet4 commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Thank you for the pull request. I was originally assuming this to be a bug, but with a deeper inspection, I understand that this makes an adjustment to the VOI LUT function from LINEAR to LINEAR_EXACT when the width is incompatible with the default LINEAR VOI LUT function, is this the case? At this point we may be dealing with a compatibility concern rather than a bug, since we're dealing with non-compliant inputs. Is there a precedent for this behavior in other tools?

@abustany

Copy link
Copy Markdown
Contributor Author

The standard says:

Window Width (0028,1051) shall always be greater than or equal to 1.

so practically, this file is not compliant...

The two other viewers I know of are:

  • Cornerstone3D, which has the same bug (I'm carrying a downstream patch I need to upstream at some point)
  • Horos, which doesn't care about LINEAR and always does LINEAR_EXACT

@Enet4 Enet4 added compatibility This concerns robustness to other DICOM implementations and removed bug This is a bug labels Sep 10, 2026
@Enet4

Enet4 commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Thank you for the clarification. This is not at all a blocker, though I find it important to recognize when a non-conformity with DICOM exists and from which side it occurs.

@abustany

Copy link
Copy Markdown
Contributor Author

Yup fully agree! We recently had to ingest a large amount of older files, and bumped into various of those "interesting" cases...

VoiLutFunction::Linear if window_level.width < 1. => {
(VoiLutFunction::LinearExact, window_level.width.max(0.))
}
VoiLutFunction::Linear => (voi_lut_function, window_level.width),

@userepo userepo Sep 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@abustany, I think this arm introduces a regression: a NaN window width used to be harmless and now breaks rendering.

On master every width went through .max(1.), and f64::max returns the non-NaN operand, so NaN silently became 1.0. With this change NaN < 1. is false, the NaN falls through to this catch-all arm, and it's stored as-is.

From there:

  • debug builds panic on debug_assert!(ww >= 1.) in window_level_linear
  • release builds fail LUT construction with CreateLutError

So a file that renders today would stop rendering.

VoiLutFunction::Linear => (voi_lut_function, window_level.width.max(1.)),

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.

hmm that's a good catch, thanks for the review. I must admit though that I don't know how we'd end up with a NaN value in there?

If I'm not mistaken, restoring .max(1.) at line 109 in the hunk above would preserve the old behavior?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

NaNs could occur from the ingestion of invalid input, either programmatically through the library core+object API or by loading a file with illegal values for Window Width (the VR DS requires numbers to be finite, but they are not inspected during DICOM parsing). On the latter case I have plans to reduce the risk of introducing NaNs at this level by adding methods to retrieve finite floating point values, so we get a recoverable error instead of a NaN.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-lib Area: library C-pixeldata Crate: dicom-pixeldata compatibility This concerns robustness to other DICOM implementations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants