fix(dicom-image-loader): use Float32Array for non-integer rescale slopes to prevent pixel data corruption#2707
Open
eugenest2557 wants to merge 3 commits into
Conversation
…pes to prevent pixel data corruption DICOM images with non-integer RescaleSlope (e.g. DTI FA maps with RescaleSlope=0.001) have their pixel data destroyed during preScale because getPixelDataTypeFromMinMax incorrectly selects Uint8Array. This happens because Number.isInteger(1.0) === true in JavaScript, so scaled min/max of 0.0 and 1.0 are treated as integers, leading to Uint8Array selection. All fractional values (0.013, 0.5, 0.999) are then truncated to 0. Fix: 1. In _handlePreScaleSetup: detect non-integer scaling parameters and force Float32Array before scaling is applied. 2. In setPixelDataType: skip re-typing if pixelData is already Float32Array, preventing the main thread from downgrading it back to Uint8Array after web worker transfer. Fixes: cornerstonejs#2706 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… division by zero The DICOM LINEAR VOI formula uses (WW-1) which becomes 0 when WW=1, producing lower === upper (degenerate range). This causes division by zero in the VOI LUT shader, resulting in a binary black/white image. Fall back to LINEAR_EXACT (lower = WC - WW/2, upper = WC + WW/2) which correctly handles WW=1 (e.g. DTI FA with WC=0.5, WW=1 gives lower=0, upper=1). Fixes: cornerstonejs#2706 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Fixes #2706
DICOM images with non-integer
RescaleSlope(e.g. DTI FA maps withRescaleSlope=0.001) render as completely black in StackViewport. These images display correctly in other viewers such as RadiAnt and OsiriX.The legacy Cornerstone had a similar issue (cornerstonejs/cornerstone#302, fixed in PR #303).
Two independent bugs are involved:
getPixelDataTypeFromMinMax(0.0, 1.0)selectsUint8ArraybecauseNumber.isInteger(1.0) === truein JavaScript. All fractional scaled values (0.013, 0.5, 0.999) are truncated to 0. This function is called twice (web worker + main threadsetPixelDataType), destroying the data at both stages.toLowHighRangewithWindowWidth=1produceslower === upper === 0via the LINEAR formula(WW-1) = 0, causing division by zero in the VOI shader and a binary black/white image.Changes & Results
_handlePreScaleSetupindecodeImageFrameWorker.js: Detect non-integer scaling parameters and forceFloat32ArraybeforegetPixelDataTypeFromMinMaxis called.setPixelDataTypeinsetPixelDataType.ts: Skip re-typing ifpixelDatais alreadyFloat32Array, preventing the main thread from downgrading it toUint8Array.toLowHighRangeinwindowLevel.ts: WhenWindowWidth <= 1with LINEAR function, fall back to LINEAR_EXACT (lower = WC - WW/2,upper = WC + WW/2) to produce a valid range.All three fixes are needed — removing any one results in either a black screen or a binary black/white image.
Testing
RescaleSlope=0.001in StackViewport — should display grayscale instead of blackRescaleSlope=1) — should be unaffectedWindowWidth > 1—toLowHighRangeshould use existing LINEAR formula unchangedChecklist
PR
semantic-release format and guidelines.
Code
etc.)
Public Documentation Updates
additions or removals.
Tested Environment
I may be wrong about some of the details — please correct me if I've misunderstood anything. Any feedback would be greatly appreciated. Thank you!