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

feat: add support for WADO-URI Streaming Volume Loading #354

Merged
merged 12 commits into from
Jan 13, 2023

Conversation

jmhmd
Copy link
Contributor

@jmhmd jmhmd commented Jan 9, 2023

Description

This PR adds support for streaming images into a volume with wadouri, i.e. a static set of dicom part 10 files on a server with a list of URIs pointing to those files.

Motivation

This may be helpful in the case where you have not extracted the series level metadata ahead of time, and you do not (or cannot) set up a dicomweb wado-rs server.

Overview of changes

The main difference is that with wadouri, you do not have a way to pre-fetch the metadata for the entire series/volume ahead of time in order to construct the volume, calculate slice spacing, reorder the images, etc.

So for this to work, we:

  • Have to assume the array of imageIds provided to the volume loader is pre-sorted by location
  • Pre-fetch the first, middle and last images of the series
  • The loader uses the first image to get series and study level metadata, uses the first and middle images to calculate slice spacing (assuming a regular spacing across the volume), uses the first and middle images to determine if the stack needs to be reversed in order, and uses the last image in the event the stack is reversed, so the correct origin is used.

Limitations

  • This will always be slower I think than using wado-rs, because each dicom file needs to be parsed instead of streaming in the pixel data directly.
  • If the correct sort order of the files is not known, the loaded images may be out of order.

This is a draft, and I have confirmed that loading works against the dcmjs.org dicomweb server in the provided example file but have not done testing with other more involved examples. I wanted to see if this approach is reasonable or if there are suggestions for improvements before adding more examples/tests.

@netlify
Copy link

netlify bot commented Jan 9, 2023

Deploy Preview for cornerstone-3d-docs ready!

Name Link
🔨 Latest commit 633c433
🔍 Latest deploy log https://app.netlify.com/sites/cornerstone-3d-docs/deploys/63c16e53420c6a000ab2db3f
😎 Deploy Preview https://deploy-preview-354--cornerstone-3d-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@jmhmd
Copy link
Contributor Author

jmhmd commented Jan 9, 2023

Of note, the image loading using wado-uri from dcmjs.org is way slower than wado-rs, slower I would think than could be explained by just the larger file size and parsing time. I think at least some of this is related to the server but not sure. It seems to be faster to load directly from and s3 bucket but I haven't done extensive testing here either.

*/
const indexesToPrefetch = [0, middleImageIndex, imageIds.length - 1];
for (let i of indexesToPrefetch) {
await imageLoader.loadImage(imageIds[i], { skipCreateImage: true }).then(
Copy link
Member

Choose a reason for hiding this comment

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

I see what you are doing here. Just an FYI we have shifted away from using loadImage directly to using imageLoadPoolManager to handle it (to respect the priority of the queue). You can see an example in the Streaming WADO RS loader. Basically we create a callback (that uses loadImage), but the job is run through imageLoadPoolManager

Copy link
Member

Choose a reason for hiding this comment

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

One more thing, the { skipCreateImage: true } doesn't do anything at all as the second parameter. In my IDE it even makes it underline red, since it is not matching the type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So when adding requests to the imageLoadPoolManager, is there a way to await those prefetched images? The reason is we have to have the metadata for those images cached before going on to construct the volume.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just pushed a commit that moves this prefetching logic to cornerstoneStreamingImageVolumeLoader.ts and detects the scheme used. Also now using imageLoadPoolManager as suggested. This seems much cleaner, the only thing is that I had to move all the logic in that file into an async function so that I could await the file prefetches before constructing the volume. The function already returned a promise, so that's ok I think, but now the .decache() and .cancel() functions are also async instead of sync.

Copy link
Member

@sedghi sedghi left a comment

Choose a reason for hiding this comment

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

I run this and it worked (it gave error for the instanceUIDs, but I got it fixed), the rendering worked fine. So great first iteration I would say

I think since it is streaming into the volume, it belongs to the same package streaming-image-volume-loader. I really like the idea that you are using the same loader scheme cornerstoneStreamingImageVolume, and we don't really care about wadoURI vs wadoRS at volume loader level.

The parts that we need to decide where to go are

  1. imageLoader.loadImage for the three imageIds: I guess the cornerstoneStreamingImageVolumeLoader can identify the wadoURI scheme and call the load via imageLoadPoolManager before calling the sortImageIdsAndGetSpacing

  2. Custom extended metadata provider: This one is tricky. I would like to get @wayfarer3130 view on this

@jmhmd
Copy link
Contributor Author

jmhmd commented Jan 10, 2023

As for the metadata provider - I am not 100% sure it is necessary, it would only be needed if there is a code path that requests an image's metadata before that image has loaded into the volume. If it only requests metadata from the 1st, middle, and possibly last images prior to loading, those will be prefetched before volume construction and the built-in metadata provider in cswid should be sufficient.

@jmhmd
Copy link
Contributor Author

jmhmd commented Jan 10, 2023

It looks like StreamingImageVolume.ts calls for each image's metadata to get scaling parameters, but at least in the example case, these never get used as successCallback only applies scaling if the image was already cached by another scheme it appears. So removing the custom extended metadata-provider still works to initially load via wadouri but I'm not sure if there will be issues with more complicated setups. I'm not familiar with the use case for that rescaling of cached images logic.

Don't hit cache to determine if all image metadata is preloaded, just detect scheme
Don't fall back to 'spacingBetweenSlices'
return {
promise: Promise.resolve(streamingImageVolume),
promise: getStreamingImageVolume(),
Copy link
Member

@sedghi sedghi Jan 11, 2023

Choose a reason for hiding this comment

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

can we say before return something like const streamingImageVolume = await getStreaming .... and then use it below directly like before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes -- how about const streamingImageVolumePromise = getStreaming... so that it's explicit? Will defer to your preference.

Remove custom metadata provider
@sedghi sedghi marked this pull request as ready for review January 12, 2023 19:18
@sedghi sedghi changed the title Feat/streaming wadouri feat: add support for WADO-URI Streaming Volume Loading Jan 13, 2023
@sedghi sedghi merged commit a1e4a36 into cornerstonejs:main Jan 13, 2023
@sedghi
Copy link
Member

sedghi commented Jan 13, 2023

@jmhmd Awesome contribution!

@jmhmd
Copy link
Contributor Author

jmhmd commented Jan 13, 2023

Thanks for merging and the great feedback! Very thankful for the amazing work you all are doing with this library.

@jmhmd jmhmd deleted the feat/streaming-wadouri branch January 13, 2023 15:31
@jmhmd jmhmd restored the feat/streaming-wadouri branch January 13, 2023 15:31
@jmhmd jmhmd deleted the feat/streaming-wadouri branch January 13, 2023 15:37
rodrigobasilio2022 pushed a commit to RadicalImaging/basilioCornerstone3D that referenced this pull request Jan 21, 2023
…s#354)

* Working wadoURI loading

* Working image loading with wadouri, but slow. Not sure if this is related to server or client implementation.

* Fix wayward closing paren

* Remove unnecessary cacheCheck param from metadata-provider

* Refactor extending metadata-provider so it's actually working.

* Move the wadouri prefetching logic
Don't hit cache to determine if all image metadata is preloaded, just detect scheme
Don't fall back to 'spacingBetweenSlices'

* Move wadoURI test imageids to demo helpers
Remove custom metadata provider

* Remove instanceUIDs.ts from example file - moved to helpers

* add example to website

* fix build
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