OHIF / Viewers

OHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages
https://docs.ohif.org/
MIT License
3.08k stars 3.25k forks source link

Bug: Stack Viewport incorrect initial VOI #3190

Closed sedghi closed 10 months ago

sedghi commented 1 year ago

Seems like our stack viewport has a wrong initial voi set.

For instance for this study https://www.dropbox.com/sh/j6mxoutxb4x3moh/AAB69hV6SfvxYj06YsuHlaoza?dl=0

Drag and drop one instance into the https://www.cornerstonejs.org/live-examples/local.html

it works correctly image

But dragging and dropping it into the v3-demo.ohif.org/local (or local server)

image

Since it works in cs3d, and not in OHIF, I expect the metadata provider of OHIF metadata for windowlevel and width is different than what cs3d has

sedghi commented 1 year ago

It is the same issue in the v2 as well, so definitely the metadata provider of OHIF getting those metadata from instance somehow wrong

image

sedghi commented 1 year ago

Seems like in the cs3d demos we are adding metadata to the wado rs metadata manager via

https://github.com/cornerstonejs/cornerstone3D-beta/blob/main/utils/demo/helpers/createImageIdsAndCacheMetaData.js#L61-L64

But OHIF doesn't do that since we use the naturalized instance via dcmjs to store metadata. So we need to see what is happening there

heyflynn commented 1 year ago

Hi @sedghi

I have been testing my own viewer with cornerstone3d using both the old wado image loader 3.3.2 and the new one 4.8.x +

I encountered several instances of this problem when the window level / center values for CT images were an array of different values. metadata was ok but createImage in image loader may be responsible. I never debug the problem beyond a user interface.

I only encountered this with 3.3.2, every version > 4 worked

sedghi commented 1 year ago

Thanks @heyflynn , we will look into this, can you be more specific which version worked? You are saying every version of wado loader > 4 works? I'm confused

heyflynn commented 1 year ago

yes. every version > 4 this was OK. I created a simple app that loaded old cornerstonejs, new cornerstone3d and the small dicom.ts renderer.

"cornerstone-wado-image-loader": "^3.0.0", wado-3

"cornerstone-wado-image-loader": "^4.10.0", wado-4

Ouwen commented 1 year ago

@sedghi, some dicom metadata stores the initial WW/WL in a different tag (sharedFunctionalGroupsSequence), is the correct initial ww/wl values stored here for the image?

    let center, width;

    try {
      const sharedFunctionalGroupsSequence = dataSet.elements.x52009229;
      const frameVOILUTSequence = sharedFunctionalGroupsSequence.items[0];
      const frameDataset = frameVOILUTSequence.dataSet.elements.x00289132;
      const windowDataset = frameDataset.items[0].dataSet;

      center = getNumberValues(windowDataset, 'x00281050', 1);
      width = getNumberValues(windowDataset, 'x00281051', 1);
    } catch {
      center = getNumberValues(dataSet, 'x00281050', 1);
      width = getNumberValues(dataSet, 'x00281051', 1);
    }
sedghi commented 1 year ago

sharedFunctionalGroupsSequence is for multiframe data, but this is happening even for the regular dicom files. @heyflynn I guess you don't have OHIF right? since it works fine inside cs3d, but NOT in OHIF. I expect the problem is the fact that in the cs3d demo we use wadors metadataManager but OHIF uses its own metadata provider which has priority and OHIF metadataProvider is based on naturalized instance of dcmjs

heyflynn commented 1 year ago

I don't have OHIF. I downloaded your files and tested them ok on both 3 and 4 of CWIL so I assume they are unrelated issues.

IbrahimCSAE commented 1 year ago

Hi @sedghi

I gave this a look. I'm not sure if it's a bug, but maybe because cornerstone3D/OHIF is using the WW/WL of the first image in the stack? I also verified dcmjs values and they look correct, so it's not dcmjs.

MR.1.3.12.2.1107.5.2.32.35162.1999123112191238865917962

It has really different WW/WL and I believe it is at the start of stack, it's WL/WW is 35/99, which matches OHIF

If you drop it into https://www.cornerstonejs.org/live-examples/local.html, you will see the same WW/WL from OHIF too.

Screenshot_3 Screenshot_2

I think cornerstone3D is causing the problem since it doesn't update the WL/WW from the metadata for each image, it's always using the first image? (should it be different for each image? RadiAnt does it that way)

https://github.com/cornerstonejs/cornerstone3D-beta/blob/5287ac179ced972f073cf8c2f962e18a63d0875f/packages/core/src/RenderingEngine/StackViewport.ts#L1807

this method called this._setPropertiesFromCache(); sets the WL/WW to the first image always, isn't this supposed to be updated for each imageId?

I removed it and added this part copied from after the if condition in the original code above ^

            const {
              imagePixelModule,
            } = this._getImageDataMetadata(image);

            const { windowCenter, windowWidth, voiLUTFunction } = imagePixelModule;
            let voiRange =
            typeof windowCenter === 'number' && typeof windowWidth === 'number'
              ? windowLevelUtil.toLowHighRange(windowWidth, windowCenter)
              : undefined;

          const isPreScaled =
            this.csImage.isPreScaled || this.csImage.preScale?.scaled;

          if (imagePixelModule.modality === 'PT' && isPreScaled) {
            voiRange = { lower: 0, upper: 5 };
          }

          this.initialVOIRange = voiRange;

          if (this.voiApplied && typeof voiRange === 'undefined') {
            voiRange = this.voiRange;
          }

          if (Object.values(VOILUTFunctionType).indexOf(voiLUTFunction) === -1) {
            this.VOILUTFunction = VOILUTFunctionType.LINEAR;
          } else {
            this.VOILUTFunction = voiLUTFunction;
          }
          this.setProperties({ voiRange });
            return;

now OHIF looks like this

demogif4

which behaves like RadiAnt viewer that I used as a reference. you will notice the WW/WL value change in the viewer overlay.

GIF 5

A fix can be added to _checkVTKImageDataMatchesCornerstoneImage() so it checks for the WW/WL everytime and returns false if different? right now this returns true since it doesn’t check for that.

https://github.com/cornerstonejs/cornerstone3D-beta/blob/5287ac179ced972f073cf8c2f962e18a63d0875f/packages/core/src/RenderingEngine/StackViewport.ts#L1401

this problem is also in the other cornerstone that isn't 3D, same behavior somewhere in the rendering functions, so I'm not sure if this really a problem, looks like it's done this way intentionally in cornerstone/cornerstone3D (use first image in stack always)?

If the value is supposed to match the first imageId always then i think there's nothing wrong with cornerstone3D/cornerstone or OHIF?, it's just the first image in the stack looks like that.

RadiAnt initially uses the values for each image, but if a user selects a custom WL/WW, then that's when it makes it uniform for everything

sedghi commented 1 year ago

@IbrahimCSAE great debugging, I came across the same conclusion yesterday as well. Interesting that you confirmed it now! We decided to apply a per-slice ww/wl on each image for the stack viewport. and as soon as the user modifies it it becomes global for the stack and later when reset happens we go back to per slice.

IbrahimCSAE commented 1 year ago

Great! Happy to help.