Skip to content

fix(nifti): pass through DICOM metadata for NIfTI-backed display sets#5970

Open
nutzlastfan wants to merge 4 commits into
OHIF:masterfrom
nutzlastfan:fix/nifti-pass-through-dicom-metadata
Open

fix(nifti): pass through DICOM metadata for NIfTI-backed display sets#5970
nutzlastfan wants to merge 4 commits into
OHIF:masterfrom
nutzlastfan:fix/nifti-pass-through-dicom-metadata

Conversation

@nutzlastfan

@nutzlastfan nutzlastfan commented Apr 20, 2026

Copy link
Copy Markdown

Context

This is a small preparatory upstream PR for a downstream NIfTI workflow.

In the downstream integration, a NIfTI-backed display set can carry DICOM-derived metadata that should be preserved for VOI / series registration instead of falling back to generic defaults.

Changes

  • add @cornerstonejs/nifti-volume-loader to the default extension package
  • if a downstream data source exposes a configured NIfTI URL for a series, pass through DICOM-derived metadata alongside the NIfTI load request

Important Note

This PR is intentionally opened as a draft because the effect depends on paired loader-side support in Cornerstone3D / @cornerstonejs/nifti-volume-loader.

Today, upstream OHIF does not yet have a full built-in NIfTI path in this area, so this branch is best viewed as a minimal handoff hook for discussion rather than as a complete end-user feature.

Why Open It Anyway

The downstream fix request specifically needs an upstream anchor in OHIF/Viewers, but I want to be explicit that the real behavior change also needs the corresponding Cornerstone3D-side implementation and review.

Verification

Code-level port only in this environment. I could not run the full local test / build toolchain from this checkout.

Summary by CodeRabbit

  • New Features
    • Added NIfTI volume support for medical imaging workflows. Image sets now dynamically load volumes from configured URLs with automatic metadata preservation from existing imaging data. Error logging provides visibility into any loading issues, expanding compatible file formats and enabling additional visualization capabilities for radiological applications.

Greptile Summary

This PR adds a NIfTI-backed display set hook to the default extension: when a data source exposes a niftiPrivateTagName config key, makeDisplaySet reads the corresponding private tag from the instance, constructs a NIfTI URL (with optional base-URL joining), and attaches a loadImageIds function that dynamically imports @cornerstonejs/nifti-volume-loader and passes DICOM-derived metadata alongside the load request.

  • niftiPrivateTagName / niftiBaseUrl are resolved fresh from the active data source inside makeDisplaySet, avoiding stale-config issues on data-source switches.
  • Errors from createNiftiImageIdsAndCacheMetadata are logged and re-thrown so callers can detect failures rather than silently receiving a partially-initialised display set.
  • @cornerstonejs/nifti-volume-loader@4.21.7 is added as a hard dependency (node_modules needed for the dynamic import()); the functional dicomMetadata pass-through depends on paired loader-side support in Cornerstone3D, as noted in the PR description.

Confidence Score: 5/5

Safe to merge as a preparatory hook; the NIfTI path is entirely inert unless a data source explicitly configures niftiPrivateTagName.

The NIfTI code path activates only when a data source provides a niftiPrivateTagName config key, leaving all existing DICOM display-set construction untouched. Previous concerns about silent error swallowing and stale config have both been addressed in this iteration. The URL-joining helpers handle the common edge cases. The dicomMetadata parameter is a forward-looking no-op until the paired loader supports it, which is explicitly called out in the PR description.

No files require special attention.

Important Files Changed

Filename Overview
extensions/default/src/getSopClassHandlerModule.js Adds optional NIfTI URL detection and a dynamic loadImageIds hook to makeDisplaySet; config is correctly resolved per-call, error is re-thrown after logging, dynamic import is used to load the nifti-volume-loader only when needed.
extensions/default/package.json Adds @cornerstonejs/nifti-volume-loader@4.21.7 as a hard dependency; the package is needed to satisfy node_modules for the dynamic import code path.

Reviews (4): Last reviewed commit: "Merge branch 'master' into fix/nifti-pas..." | Re-trigger Greptile

@netlify

netlify Bot commented Apr 20, 2026

Copy link
Copy Markdown

Deploy Preview for ohif-dev canceled.

Name Link
🔨 Latest commit 1d61e11
🔍 Latest deploy log https://app.netlify.com/projects/ohif-dev/deploys/6a1c7b10f7cfd00008e33ef7

@nutzlastfan nutzlastfan marked this pull request as ready for review April 20, 2026 06:41
Comment thread extensions/default/src/getSopClassHandlerModule.js Outdated
Comment thread extensions/default/src/getSopClassHandlerModule.js
Comment thread extensions/default/src/getSopClassHandlerModule.js
Comment thread extensions/default/src/getSopClassHandlerModule.js Outdated

Copy link
Copy Markdown
Author

Tracked down the regression from downstream testing: the NIfTI datasource config was being resolved once at module initialization time. In our deployed app that could leave niftiPrivateTagName / niftiBaseUrl empty, so OHIF silently fell back to the regular DICOM stack path and loaded individual instances instead of the NIfTI volume. This follow-up commit resolves the datasource config when building each display set again, which restores the working behavior without widening the PR scope.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR integrates NIfTI volume loader support into the OHIF Viewers by adding a new dependency, implementing URL path helpers, reading NIfTI configuration from the data source, and overriding the image loading pipeline to dynamically import and use the NIfTI loader when a NIfTI URL is available.

Changes

NIfTI Volume Loader Integration

Layer / File(s) Summary
NIfTI URL path utilities
extensions/default/src/getSopClassHandlerModule.js
Adds isAbsolutePathOrUrl and joinUrl helpers to normalize and construct absolute NIfTI URLs from base paths and instance-provided path values.
NIfTI configuration and URL resolution
extensions/default/src/getSopClassHandlerModule.js
Within makeDisplaySet, extracts niftiPrivateTagName and niftiBaseUrl from the active data source config, reads the corresponding instance field, and computes niftiURL only when the configured tag exists and resolves to a non-empty string.
NIfTI image loading and metadata integration
extensions/default/src/getSopClassHandlerModule.js
When niftiURL is present, overrides loadImageIds to dynamically import @cornerstonejs/nifti-volume-loader, prepare DICOM metadata, call createNiftiImageIdsAndCacheMetadata, and update imageSet with returned image IDs, frame count, and reconstructability flag; errors are logged and rethrown.
NIfTI loader package dependency
extensions/default/package.json
Adds @cornerstonejs/nifti-volume-loader version 4.21.7 to the dependencies section.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A rabbit hops through volumes new,
NIfTI paths resolved just right,
Metadata bundled, loaders imported true,
Image IDs dancing in reconstructed light! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(nifti): pass through DICOM metadata for NIfTI-backed display sets' accurately describes the main change: enabling NIfTI-backed display sets to carry DICOM-derived metadata. It follows semantic-release format and is clear and specific.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description check ✅ Passed The PR description provides clear context, lists changes, includes important caveats about draft status and dependencies, and documents verification limitations.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

🤖 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 `@extensions/default/src/getSopClassHandlerModule.js`:
- Around line 180-184: The display set currently forces isReconstructable: true
regardless of the loader result; instead derive it from
createNiftiImageIdsAndCacheMetadata's resolved value (loadedImageIds) by setting
isReconstructable to true only when Array.isArray(loadedImageIds) &&
loadedImageIds.length > 0; update the call to imageSet.setAttributes (and ensure
numImageFrames uses the same safe length logic) so downstream MPR/volume code
sees reconstructability only when there are actual image IDs.
🪄 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: 2a82acd1-d86e-47cf-9ff3-c2b25e65339e

📥 Commits

Reviewing files that changed from the base of the PR and between 0885468 and 1d61e11.

📒 Files selected for processing (2)
  • extensions/default/package.json
  • extensions/default/src/getSopClassHandlerModule.js

Comment on lines +180 to +184
imageSet.setAttributes({
imageIds: loadedImageIds,
numImageFrames: Array.isArray(loadedImageIds) ? loadedImageIds.length : 0,
isReconstructable: true,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don't force isReconstructable: true when the loader returns no image IDs.

If createNiftiImageIdsAndCacheMetadata resolves to an empty (or non-array) result, the display set ends up with 0 frames yet still advertises reconstructability, which can mislead downstream MPR/volume paths. Derive the flag from the resolved IDs instead.

🐛 Proposed fix
+          const hasImageIds = Array.isArray(loadedImageIds) && loadedImageIds.length > 0;
+
           imageSet.setAttributes({
             imageIds: loadedImageIds,
-            numImageFrames: Array.isArray(loadedImageIds) ? loadedImageIds.length : 0,
-            isReconstructable: true,
+            numImageFrames: hasImageIds ? loadedImageIds.length : 0,
+            isReconstructable: hasImageIds,
           });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
imageSet.setAttributes({
imageIds: loadedImageIds,
numImageFrames: Array.isArray(loadedImageIds) ? loadedImageIds.length : 0,
isReconstructable: true,
});
const hasImageIds = Array.isArray(loadedImageIds) && loadedImageIds.length > 0;
imageSet.setAttributes({
imageIds: loadedImageIds,
numImageFrames: hasImageIds ? loadedImageIds.length : 0,
isReconstructable: hasImageIds,
});
🤖 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 `@extensions/default/src/getSopClassHandlerModule.js` around lines 180 - 184,
The display set currently forces isReconstructable: true regardless of the
loader result; instead derive it from createNiftiImageIdsAndCacheMetadata's
resolved value (loadedImageIds) by setting isReconstructable to true only when
Array.isArray(loadedImageIds) && loadedImageIds.length > 0; update the call to
imageSet.setAttributes (and ensure numImageFrames uses the same safe length
logic) so downstream MPR/volume code sees reconstructability only when there are
actual image IDs.

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.

1 participant