Skip to content

fix: stack viewport colormap preserve and 4d local improved support#2679

Merged
sedghi merged 4 commits into
mainfrom
ptmetadata
Jun 20, 2026
Merged

fix: stack viewport colormap preserve and 4d local improved support#2679
sedghi merged 4 commits into
mainfrom
ptmetadata

Conversation

@sedghi

@sedghi sedghi commented Mar 29, 2026

Copy link
Copy Markdown
Member
  1. In stack viewport if we have different types of data colormap change reset on slice changes

  2. 4d for local drag and drop does not have good support

Ask alireza for data

Summary by CodeRabbit

  • New Features

    • Enhanced viewport property preservation to restore colormap, sharpening, and smoothing settings
    • Added support for frame parameters in multiple URL formats
  • Bug Fixes

    • Fixed viewport properties preservation when re-initializing with different data types
    • Improved multiframe image data type handling
  • Tests

    • Added test validating viewport property preservation during stack re-initialization
    • Enhanced multiframe frame parameter handling test coverage

sedghi added 2 commits March 22, 2026 09:01
…uring actor recreation

- Updated _setPropertiesFromCache to retrieve additional properties (colormap, sharpening, smoothing) from cache.
- Modified logic in getCameraCPU to ensure stack properties are maintained when actor recreation is triggered by data type mismatches.
- Added unit test to verify preservation of stack properties under specific conditions.
- Enhanced fakeImageLoader to support multiple data types for testing purposes.
…rmats

- Updated `generateFrameImageId` to support DICOMweb and local loader image IDs, allowing for frame number replacement and appending.
- Improved error handling by removing the requirement for a specific "/frames/" pattern in the base image ID.
- Added unit tests to verify the new functionality, including handling of query parameters and local file image IDs.
- Enhanced `retrieveFrameParameterIndex` and `isLoaded` functions to utilize regex for frame parameter detection.
@sedghi sedghi changed the title ptmetadata fix: stack viewport colormap preserve and 4d local improved support Mar 29, 2026

@jbocce jbocce 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.

Overall the changes seem fine. I tested and the colormap example no longer resets while navigating the stack. However, the two (formerly) non-readable dicom files are still non-readable.

Something to consider beyond the changes you made...

in retrieveMultiframeDataset.ts, generateMultiframeWADOURIs still appends &frame=${i} unconditionally. If the base URI does not already contain query params, that may produce malformed IDs. Might be worth normalizing with ? vs & there as well.

@jbocce jbocce 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.

The code is approved. However the OHIF downstream validation is failing.

Resolve conflict in packages/core/src/utilities/splitImageIdsBy4DTags.ts:
main moved the implementation into @cornerstonejs/metadata (core file is now
a deprecated re-export shim). Ported the PR's generateFrameImageId enhancement
(query-param + local-file frame addressing) into the new metadata home.
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

StackViewport now snapshots stackInvalidated before mutations to correctly branch VOI/invert re-initialization: on a first-time invalidation it restores all cached rendering properties instead of recomputing them. Separately, generateFrameImageId, dataSetCacheManager, and retrieveMultiframeDataset are updated to support frame= query parameters so local loader imageIds (e.g., dicomfile:0) can be split into per-frame 4D groups.

Changes

StackViewport Property Preservation

Layer / File(s) Summary
wasStackInvalidated snapshot and conditional VOI/invert restoration
packages/core/src/RenderingEngine/StackViewport.ts
Captures stackInvalidated before mutations; expands _setPropertiesFromCache to restore VOILUTFunction, colormap, interpolationType, invert, sharpening, and smoothing; branches the VOI/invert re-init path on wasStackInvalidated so cached properties are restored instead of recomputed on first invalidation.
Fake image loader dataType support and GPU render test
utils/test/testUtilsImageLoader.js, packages/core/test/stackViewport_gpu_render_test.js
Adds typed-array constructor resolution and bitsAllocated derivation to fakeImageLoader/fakeMetaDataProvider to allow varying dataType per imageId; asserts that colormap, interpolationType, voiRange, and invert survive actor re-initialization triggered by a dataType mismatch.

Local imageId 4D Frame Query Parameter Support

Layer / File(s) Summary
generateFrameImageId multi-mode and frame query param detection
packages/metadata/src/utilities/splitImageIdsBy4DTags.ts, packages/dicomImageLoader/src/imageLoader/wadouri/dataSetCacheManager.ts, packages/dicomImageLoader/src/imageLoader/wadouri/retrieveMultiframeDataset.ts
Reworks generateFrameImageId to replace /frames/N, replace existing frame= query params, or append ?frame=N/&frame=N; broadens dataSetCacheManager detection from &frame= to [?&]frame=; updates retrieveFrameParameterIndex regex to match both ?frame= and &frame= forms.
4D frame query param splitting tests
packages/core/test/utilities/splitImageIdsBy4DTags.jest.js
Replaces the throws-on-missing-/frames/ assertion with positive cases for query-param and dicomfile: base imageIds in generateFrameImageId; adds a handleMultiframe4D test for dicomfile:0 inputs split into dicomfile:0?frame=N groups.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • wayfarer3130

Poem

🐇 Hop along the stack I go,
Cached colors in tow—invert and glow!
No frames path? No fuss, no throw,
?frame=1 will do the show.
Properties saved, the actors spin,
A bunny's fix from deep within! 🎨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete and vague, missing critical sections like Context (issue links), detailed Changes & Results, Testing instructions, and checklist items required by the template. Add Context with issue links (Fixes #ISSUE_NUMBER), detailed Changes & Results explaining the colormap preservation and 4D support changes, specific Testing steps, and complete all checklist items from the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is partially related to the changeset, referring to real aspects (stack viewport colormap preservation and 4D local support) but lacks specificity about the technical implementation.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ptmetadata

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/dicomImageLoader/src/imageLoader/wadouri/retrieveMultiframeDataset.ts (1)

29-38: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve non-frame query params when frame is first in the query string.

With the new ?[&]frame= support, URIs like ... ?frame=2&token=abc now match, but multiframeURI = uri.slice(0, frameParameterIndex) drops token=abc. That can break loadedDataSets lookup for valid signed/query-bearing URIs.

Suggested fix
 function retrieveMultiframeDataset(uri) {
-  const frameParameterIndex = retrieveFrameParameterIndex(uri);
-  const multiframeURI =
-    frameParameterIndex === -1 ? uri : uri.slice(0, frameParameterIndex);
-  const frame = parseInt(uri.slice(frameParameterIndex + 7), 10) || 1;
+  const [baseURI, queryString = ''] = uri.split('?');
+  const params = queryString ? queryString.split('&') : [];
+  const frameParam = params.find((p) => p.startsWith('frame='));
+  const frame = parseInt(frameParam?.slice('frame='.length), 10) || 1;
+  const remainingParams = params.filter((p) => !p.startsWith('frame='));
+  const multiframeURI = remainingParams.length
+    ? `${baseURI}?${remainingParams.join('&')}`
+    : baseURI;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/dicomImageLoader/src/imageLoader/wadouri/retrieveMultiframeDataset.ts`
around lines 29 - 38, The issue is that when frame is the first query parameter
(e.g., ?frame=2&token=abc), using uri.slice(0, frameParameterIndex) truncates
the URI and loses subsequent query parameters. Instead of slicing at the match
index, extract and remove only the frame parameter portion (including its
separator) while preserving other query parameters. In the
retrieveMultiframeDataset function, replace the uri.slice approach with logic
that identifies the full frame parameter string (with its ? or & prefix and
value) and removes only that portion from the URI, leaving the multiframeURI
intact with all other query parameters preserved for proper loadedDataSets
lookup.
🧹 Nitpick comments (2)
packages/core/src/RenderingEngine/StackViewport.ts (1)

1002-1008: ⚡ Quick win

Avoid unconditional re-renders while restoring sharpening/smoothing cache.

getProperties() always returns numeric sharpening/smoothing, so both branches always execute and each setter triggers render() during cache restore, even when values did not change. This adds unnecessary render work in actor re-init paths.

♻️ Proposed change
-    if (typeof sharpening !== 'undefined') {
-      this.setSharpening(sharpening);
-    }
-
-    if (typeof smoothing !== 'undefined') {
-      this.setSmoothing(smoothing);
-    }
+    if (typeof sharpening !== 'undefined' && this.sharpening !== sharpening) {
+      this.sharpening = sharpening;
+    }
+
+    if (typeof smoothing !== 'undefined' && this.smoothing !== smoothing) {
+      this.smoothing = smoothing;
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/RenderingEngine/StackViewport.ts` around lines 1002 - 1008,
The setSharpening and setSmoothing methods are being called unconditionally
during cache restoration even when the values haven't changed, causing
unnecessary render calls. Instead of checking only if the properties are
defined, compare the current sharpening and smoothing values (via appropriate
getter methods) with the incoming values and only call setSharpening and
setSmoothing if the values have actually changed. This prevents triggering
render work when restoring cached values that are identical to the current
state.
packages/core/test/stackViewport_gpu_render_test.js (1)

896-913: ⚡ Quick win

Extend this regression test to cover all newly restored properties.

The implementation now restores VOILUTFunction, sharpening, and smoothing too, but this test currently only checks colormap/interpolation/VOI/invert. Adding those assertions will lock in the full contract of this PR path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/test/stackViewport_gpu_render_test.js` around lines 896 - 913,
The regression test in the then() block currently only asserts that colormap,
interpolationType, voiRange, and invert properties are restored after calling
setImageIdIndex(1), but the implementation now also restores VOILUTFunction,
sharpening, and smoothing properties. Add three additional expect statements in
the then() block after the existing assertions to verify that
props.VOILUTFunction, props.sharpening, and props.smoothing all match their
expected values. This ensures the full contract of the property restoration
behavior is validated by the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@utils/test/testUtilsImageLoader.js`:
- Around line 190-193: The highBit property in the fake imagePixelModule
metadata is incorrectly set to bitsAllocated when it should represent the
zero-indexed position of the highest bit. In the test utility where
bitsAllocated, bitsStored, and highBit are defined together, change highBit from
bitsAllocated to bitsAllocated - 1 to correctly represent the bit index (for
example, 8-bit should have highBit of 7, not 8).

---

Outside diff comments:
In
`@packages/dicomImageLoader/src/imageLoader/wadouri/retrieveMultiframeDataset.ts`:
- Around line 29-38: The issue is that when frame is the first query parameter
(e.g., ?frame=2&token=abc), using uri.slice(0, frameParameterIndex) truncates
the URI and loses subsequent query parameters. Instead of slicing at the match
index, extract and remove only the frame parameter portion (including its
separator) while preserving other query parameters. In the
retrieveMultiframeDataset function, replace the uri.slice approach with logic
that identifies the full frame parameter string (with its ? or & prefix and
value) and removes only that portion from the URI, leaving the multiframeURI
intact with all other query parameters preserved for proper loadedDataSets
lookup.

---

Nitpick comments:
In `@packages/core/src/RenderingEngine/StackViewport.ts`:
- Around line 1002-1008: The setSharpening and setSmoothing methods are being
called unconditionally during cache restoration even when the values haven't
changed, causing unnecessary render calls. Instead of checking only if the
properties are defined, compare the current sharpening and smoothing values (via
appropriate getter methods) with the incoming values and only call setSharpening
and setSmoothing if the values have actually changed. This prevents triggering
render work when restoring cached values that are identical to the current
state.

In `@packages/core/test/stackViewport_gpu_render_test.js`:
- Around line 896-913: The regression test in the then() block currently only
asserts that colormap, interpolationType, voiRange, and invert properties are
restored after calling setImageIdIndex(1), but the implementation now also
restores VOILUTFunction, sharpening, and smoothing properties. Add three
additional expect statements in the then() block after the existing assertions
to verify that props.VOILUTFunction, props.sharpening, and props.smoothing all
match their expected values. This ensures the full contract of the property
restoration behavior is validated by the test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 65601f7e-8294-4175-8e13-99a88f00238e

📥 Commits

Reviewing files that changed from the base of the PR and between 53a356d and 2dbd92f.

📒 Files selected for processing (7)
  • packages/core/src/RenderingEngine/StackViewport.ts
  • packages/core/test/stackViewport_gpu_render_test.js
  • packages/core/test/utilities/splitImageIdsBy4DTags.jest.js
  • packages/dicomImageLoader/src/imageLoader/wadouri/dataSetCacheManager.ts
  • packages/dicomImageLoader/src/imageLoader/wadouri/retrieveMultiframeDataset.ts
  • packages/metadata/src/utilities/splitImageIdsBy4DTags.ts
  • utils/test/testUtilsImageLoader.js

Comment thread utils/test/testUtilsImageLoader.js
@sedghi sedghi merged commit 5f192ff into main Jun 20, 2026
13 of 14 checks passed
@sedghi sedghi deleted the ptmetadata branch June 20, 2026 19:20
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