Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Developed NIFTI 4d viewer with voxel intensity plots. #84

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

AhmedBasem20
Copy link
Contributor

Added a new section to the TF2.4 website (the final piece of GSoC):
image

Features

  • Multi-viewport Visualization Users can upload a NIFTI file, which is displayed in three synchronized viewports (axial, sagittal, and coronal) using the CornerstoneJS suite.
  • Voxel Intensity Plot A voxel intensity plot will be generated and displayed when you drag or scroll through an image viewport.
  • For now, the intensity values are on the Y axis, and the total time points on the X axis.

Testing

I used software like ITK-SNAP and MRIcroGL to test the accuracy of the cursor position and the intensity plots and I got accurate results.

image

TODO

  • Update the cursor position and the intensity plot on mouse click event (for now it is updated on scroll and mouse drag only)
  • Create a pipeline to generate NIFTI files from the repository and display it initially on the viewer, thoughts?
  • Improve the intensity plot by adding corresponding b-values on the X axis and the fitted curve from d, dp, and f images.

Demo:

https://ahmedbasem20.github.io/TF2.4_IVIM-MRI_CodeCollection/nifti-viewer/

Copy link
Contributor

@etpeterson etpeterson left a comment

Choose a reason for hiding this comment

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

Nicely interactive but I found a few bugs.

const trace = {
y: values,
mode: 'markers',
type: 'bar',
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably want a line or scatter plot here.

cornerstoneNIFTIImageLoader.nifti.loadHeader(imageId).then((header) => {
dims = [...header.voxelLength, header.timeSlices];
loadAndViewImage(document.getElementById('nifti-image-z'), `${imageId}#z,t-0`, 'axial');
loadAndViewImage(document.getElementById('nifti-image-x'), `${imageId}#x,t-0`, 'sagittal');
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see anything in this view.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a bug in synchronizing the sagittal view initially that I'm still trying to figure out.
For now you can fix this by scrolling through the view that is not synched, it will work normally after that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually I'm noticing it's both the sagittal and coronal views that don't act correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please share with me the image so I can try debugging. (I can't find this issue with the images I'm using)

Copy link
Contributor

Choose a reason for hiding this comment

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

Same problem sharing images, sorry. Now I'm not seeing the coronal view misbehave. Definitely the sagittal one though. Sounds like you see that too though.

imageIds: Array.from({ length: numberOfSlices }, (_, i) => `nifti:${imageIdObject.filePath}#${imageIdObject.slice.dimension}-${i},t-0`)
};

cornerstoneTools.addStackStateManager(element, ['stack']);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we also want a rescale tool, sometimes I can barely see anything in those images.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean a tool for zooming in/out, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

No, brightness/contrast is what they seem to call it here. https://www.cornerstonejs.org/live-examples/videocolor

Copy link
Contributor Author

Choose a reason for hiding this comment

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

got it, will work on that.

}

// Extract voxel values across all time points
function getVoxelValuesAcrossTime(x, y, z, nx, ny, nz, nt) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't seem to be correct, select bright parts of images but the value is low. I select dark parts of the image but the value is high. Most likely x, y, and z aren't what you think they are. But also is there no cornerstone based way to do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The current voxel values are extracted from cornerstone events cornerstonestackscroll and cornerstonetoolsmousedrag. But I found that I was reducing 1 from the extracted values that's what was causing the issue. I fixed that and it is more precise now.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's still not working. See how I have a very bright part selected but the values are quite low. Other areas show up much higher on the plot than this.
I'm guessing it's either image orientation differences or still some indexing problems. I don't see how you're handling the orientation differences, so that's one place to start. What I mean by that is that indexing into the raw data doesn't necessarily equate to the displayed x, y, z. There's a possible axis flip and permutation based on the raw data so it's always displayed in the correct orientation.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is orientation difference and handled it here:

voxelX = Math.min(Math.max(Math.round(voxelX), 1), nx);
voxelY = Math.min(Math.max(ny - Math.round(voxelY), 1), ny);
voxelZ = Math.min(Math.max(nz - Math.round(voxelZ), 1), nz);

I used the complement of y and z due to the axis flip. and the indexing seems accurate according to the images I tested. Can you share the image you're using here so that I can reproduce the issue?

Copy link
Contributor

@etpeterson etpeterson Oct 18, 2024

Choose a reason for hiding this comment

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

I can't share the images because of patient privacy, but you can't count on that transformation always being the same. I opened the same image in yours and a full viewer and I see the images and notations are flipped left/right. Here's a screenshot showing that and some header info.
image

But what you want is to rely on cornerstone to do that stuff, not reinvent it yourself. https://docs.cornerstonejs.org/concepts/pixel-coordinate-system.html

@etpeterson
Copy link
Contributor

Hey @AhmedBasem20 are you going to be able to get more work done on this?

@AhmedBasem20
Copy link
Contributor Author

Hi @etpeterson Sorry I've been very busy recently but hopefully I'll get more work in and address your comments next week.

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