Skip to content

Conversation

@mbellehumeur
Copy link
Contributor

@mbellehumeur mbellehumeur commented Feb 10, 2026

Context

This PR adds a 'Volume Options' menu in the volume3d viewport to control volume decimation and downsampling:

image image image

Changes & Results

Automatic decimation is applied to volume and volume3d viewports when the number of voxel is higher than a threshold set in the configuration file with name volumeAutoDecimationThreshold .

Testing

To enable auto decimation
Set volumeAutoDecimationThreshold to a number (e.g. 500 million) in configuration file:
volumeAutoDecimationThreshold: 500_000_000,
Any volume whose estimated voxel count is above this value will be decimated when loaded (e.g. downsized so the resulting voxel count is at or below the threshold). The exact decimation (in-plane and slice) is chosen automatically.
Disable auto decimation
Omit volumeAutoDecimationThreshold from the config, or set it to null / undefined. No automatic decimation will be applied; volumes load at full resolution (subject to other limits in your app).

Manually changing the decimation can be done in the 3D viewport.

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.)

Public Documentation Updates

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

Tested Environment

  • [] OS:
  • [] Node version:
  • [] Browser:

@netlify
Copy link

netlify bot commented Feb 10, 2026

Deploy Preview for ohif-dev canceled.

Name Link
🔨 Latest commit c491996
🔍 Latest deploy log https://app.netlify.com/projects/ohif-dev/deploys/698d99d44196850008bd1ee6

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

16 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +15 to +18
/** Parses IJK decimation from volumeId only when format is decimatedVolumeLoader:baseVolumeId:i_j_k; otherwise [1,1,1]. */
function getIjkDecimationFromVolumeId(volumeId: string): [number, number, number] {
const parts = volumeId.split(':');
if (parts.length < 3 || parts[0] !== 'decimatedVolumeLoader') {
Copy link

Choose a reason for hiding this comment

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

Decimation parsing always wrong

getIjkDecimationFromVolumeId assumes decimatedVolumeLoader:baseVolumeId:i_j_k is 3 parts and uses parts[2] as the i_j_k suffix, but reloadVolumeWithDecimation generates IDs like decimatedVolumeLoader:${baseVolumeId}:${i}_${j}_${k} where baseVolumeId itself is schema:uid (contains :). That makes parts[2] the displaySet UID, not the i_j_k suffix, so this function will always fall back to [1,1,1] and the UI will misreport current decimation / dimensions.

Comment on lines +1548 to +1552
const idParts = currentVolumeId.split(':');
const baseVolumeId = idParts.length > 1 ? idParts[1] : displaySetInstanceUID;
const decimationSuffix = `${i}_${j}_${k}`;
const newVolumeId = `decimatedVolumeLoader:${baseVolumeId}:${decimationSuffix}`;

Copy link

Choose a reason for hiding this comment

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

New volumeId is malformed

reloadVolumeWithDecimation builds baseVolumeId via currentVolumeId.split(':')[1], which breaks as soon as the volumeId has more than one : segment (e.g. decimatedVolumeLoader:<displaySetUID>:volume3d or any schema:uid:suffix). This can drop the loader scheme / suffix and produce an invalid newVolumeId, leading to cache misses or loading the wrong volume when reloading with decimation.

Comment on lines +135 to 136
this.volumeImageIds.delete(invalidatedDisplaySetInstanceUID);

Copy link

Choose a reason for hiding this comment

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

Cache invalidation leaves stale map entry

invalidateViewportData stores volumeImageIds in this.volumeImageIds keyed by displaySet.displaySetInstanceUID (see _getVolumeViewportData), but deletes with this.volumeImageIds.delete(invalidatedDisplaySetInstanceUID) only after switching to candidateVolumeIds deletion. This is fine only if the key is always the displaySet UID; however earlier code deleted by volumeId, and this method still treats invalidatedDisplaySetInstanceUID as a volume identifier in other spots. If volumeImageIds was previously written using volumeId keys (from older cached state), this deletion won’t clear it and the reload path will incorrectly think the volume is already cached.

Comment on lines 388 to 392
displaySetService._broadcastEvent(
displaySetService.EVENTS.DISPLAY_SETS_CHANGED,
displaySetService.getActiveDisplaySets()
);

Copy link

Choose a reason for hiding this comment

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

Uses private displaySetService API

volumeLoadedHandler calls displaySetService._broadcastEvent(...). This is a private method (underscore-prefixed) and not part of the public service contract, so it’s likely to break on service refactors and can’t be relied on for extensions. If consumers depend on DISPLAY_SETS_CHANGED, this should be emitted via the public API (or the displaySet should be updated through the normal service pathways) rather than reaching into a private method.

Comment on lines 192 to 197
volumeLoader.registerVolumeLoader(
'cornerstoneStreamingImageVolume',
cornerstoneStreamingImageVolumeLoader
(volumeId: string, options: any) => {
return decimatedVolumeLoader(volumeId, options);
}
);
Copy link

Choose a reason for hiding this comment

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

Overrides standard volume loader

volumeLoader.registerVolumeLoader('cornerstoneStreamingImageVolume', ...) now returns decimatedVolumeLoader instead of cornerstoneStreamingImageVolumeLoader. Any callsites expecting the streaming loader’s semantics (progressive streaming, options, or volumeId format) will now get a different loader implementation, which can cause functional regressions outside the decimation feature. If this is intentional, it needs a narrower opt-in (e.g., only for specific schemes) rather than replacing a core loader by name.

…nfig threshold and refactoring related functions
@sedghi
Copy link
Member

sedghi commented Feb 10, 2026

please sync with @dan-rukas i'm pretty sure he can help you with UI

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