Skip to content

fix(dicomImageLoader): reset planarConfiguration to 0 after JPEG Lossless decode#2778

Merged
sedghi merged 2 commits into
cornerstonejs:mainfrom
CIick:fix/jpeg-lossless-planar-configuration
Jun 30, 2026
Merged

fix(dicomImageLoader): reset planarConfiguration to 0 after JPEG Lossless decode#2778
sedghi merged 2 commits into
cornerstonejs:mainfrom
CIick:fix/jpeg-lossless-planar-configuration

Conversation

@CIick

@CIick CIick commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Context

jpeg-lossless-decoder-js always outputs pixel-interleaved data (RGBRGBRGB…) regardless of the DICOM Planar Configuration tag (0028,0006). This is hardcoded in its setValueRGB implementation: outputData[3 * pixelIndex + componentIndex] = value

However, when a DICOM file has Planar Configuration = 1 (band-interleaved: RRRR…GGGG…BBBB), that value is read into imageFrame.planarConfiguration before decoding and was never corrected afterwards.

Downstream, convertColorSpace reads imageFrame.planarConfiguration and calls either convertRGBColorByPixel (PC=0) or convertRGBColorByPlane (PC=1). When PC is left as 1, convertRGBColorByPlane is called on interleaved data,
producing severe rainbow colour artifacts.

Per DICOM PS 3.5 §8.2.1, Planar Configuration is not meaningful for compressed transfer syntaxes and shall be ignored. The decoder output format governs.

Affected transfer syntaxes:

  • 1.2.840.10008.1.2.4.57 -> JPEG Lossless (Process 14)
  • 1.2.840.10008.1.2.4.70 -> JPEG Lossless SV1 (Process 14 SV1)

Example Artifacts:

  • Before cornerstone changes
  • image
  • After cornerstone changes
  • image

Changes & Results

Change: One line added to packages/dicomImageLoader/src/shared/decoders/decodeJPEGLossless.ts. We force imageFrame.planarConfiguration = 0 after the decoder runs and before pixelData is assigned, so the downstream color space converter always takes the correct pixel-interleaved path.

Before: RGB images stored with JPEG Lossless transfer syntax and Planar Configuration = 1 rendered with severe rainbow color artifacts.

After: Those images render correctly regardless of the Planar Configuration value in the DICOM header.

Related: The same issue almost certainly exists in decodeJPEGLS, decodeJPEG2000, and decodeHTJ2K, which likewise do not reset planarConfiguration after decoding. This was confirmed for decodeJPEG2000: manually setting planarConfiguration=1 on a JPEG 2000 RGB image reproduced the same rainbow artifacts. Those decoders are left for a follow-up as test images directly from the modality were not obtained. If this fix is acceptable for JPEG Lossless decoding, I'd gladly add it to the other decoders.

Testing

Two anonymized 1.2.840.10008.1.2.4.70 DICOM files are attached below, one with Planar Configuration = 1 (reproduces the rainbow artifact on the unfixed build) and one with Planar Configuration = 0 (correct reference rendering).

  1. Load the Planar Configuration = 1 file into a Cornerstone viewer built from this branch and the image should render correctly (no rainbow artifacts).
  2. Load the same file against the unfixed main branch to confirm the artifact is present and this fix resolves it.
  3. Load the Planar Configuration = 0 file on both builds, rendering should be identical, confirming no regression.

Example Redacted Files.zip

Checklist

PR

  • My Pull Request title is descriptive, accurate and follows the
    semantic-release format and guidelines.

Code

  • My code has been well-documented (function documentation, inline comments,
    etc.)
  • I did have a comment in the code, however I pulled it out to explain it here, explaining how Planar Configuration is meant to be ignored according to the DICOM standard. Referencing DICOM PS3.3 § C.7.6.3 which explains, "Indicates whether the pixel data are encoded color-by-plane or color-by-pixel." As well as pointing out the paragraph in DICOM PS 3.5 §8.2.1.) which states specifically, "JPEG compressed data streams are always color-by-pixel and should be specified as such (a decoder can essentially ignore this Data Element however as the value for JPEG compressed data is already known)

Public Documentation Updates

  • The documentation page has been updated as necessary for any public API
    additions or removals.

Tested Environment

  • "OS: Windows 11 Pro"
  • "Node version: v22.22.0. Test on older version of cornerstone but this issue happens on the live OHIF local viewer
  • "Browser: Microsoft Edge v149.0.4022.98 (Official build) (64-bit)

Summary by CodeRabbit

  • Bug Fixes
    • Improved JPEG-lossless image decoding so pixel layout is now set correctly before image data is assigned. This helps ensure decoded images display more reliably.

…less decode

jpeg-lossless-decoder-js always outputs pixel-interleaved data (RGBRGB...)
regardless of the DICOM Planar Configuration tag (0028,0006). When that tag
is 1 (band-interleaved), downstream convertColorSpace incorrectly calls
convertRGBColorByPlane on interleaved data, producing severe rainbow color
artifacts on RGB images encoded with transfer syntaxes 1.2.840.10008.1.2.4.57
and 1.2.840.10008.1.2.4.70.

The same issue almost certainly exists in decodeJPEGLS, decodeJPEG2000, and
decodeHTJ2K, which likewise do not reset planarConfiguration after decoding.
This was confirmed for decodeJPEG2000: manually setting planarConfiguration=1
on a JPEG 2000 RGB image reproduced the same rainbow artifacts. Those decoders
are not changed here as test images directly from the modality were not obtained
to verify the fix end-to-end.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ed9308ba-9b89-451b-94bf-067a1652a336

📥 Commits

Reviewing files that changed from the base of the PR and between 902d3ae and b852240.

📒 Files selected for processing (1)
  • packages/dicomImageLoader/src/shared/decoders/decodeJPEGLossless.ts

📝 Walkthrough

Walkthrough

In decodeJPEGLossless.ts, imageFrame.planarConfiguration is explicitly set to 0 immediately after JPEG-lossless decompression, before pixelData is assigned based on pixelRepresentation and bitsAllocated.

Changes

JPEG-lossless decoder planarConfiguration fix

Layer / File(s) Summary
Force planarConfiguration=0 after decompression
packages/dicomImageLoader/src/shared/decoders/decodeJPEGLossless.ts
Sets imageFrame.planarConfiguration = 0 after decompression and before the typed array pixel data assignment.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Poem

A pixel array, once confused by its plane,
Now knows it is zero — no jumbled refrain.
The lossless decoder stood tall and said, "Hey!
Configuration zero will save the day!"
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, specific, and accurately describes the main JPEG Lossless planarConfiguration fix.
Description check ✅ Passed The description includes the required context, changes/results, testing steps, and checklist items from the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sedghi sedghi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Love it thanks

@sedghi sedghi merged commit 4b413f6 into cornerstonejs:main Jun 30, 2026
15 of 17 checks passed
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.

2 participants