How to load nifti file from web browser link?

Hi there,
I have a nifti file (xxx.nii.gz) and can open it in 3D slicer with drag and drop the file to the app.
I watched the video (Using 3D Slicer with cloud DICOMweb databases - YouTube) and found a DICOM can be opened from web browser lnk. Now, I want to load a nifti file in 3D slicer just like that. Is it possible?

I noticed a link looks like slicer://viewer?studyUID=xxx&access_token=xxx&dicomweb_endpoint=httpsxxx&dicomweb_uri_endpoint=httpsxxx, but couldn’t find a document what value should be passed for each argment.

This is easily doable. All you need to do is to create a small Python scripted module that handles this URL. You can generate the skeleton of the module using Extension Wizard and then you would only keep the module class that would set up the callback function as it is done in the DICOM module.

Your callback function could be something like this (you can copy-paste this code into the Python console to test it):

def reportProgress(message, level, sampleDataLogic):
    # Print progress in the console
    print("Loading... {0}%".format(sampleDataLogic.downloadPercent))
    # Abort download if cancel is clicked in progress bar
    progressWindow = sampleDataLogic.progressWindow
    if progressWindow.wasCanceled:
        raise Exception("download aborted")
    # Update progress window
    progressWindow.show()
    progressWindow.activateWindow()
    progressWindow.setValue(int(sampleDataLogic.downloadPercent))
    progressWindow.setLabelText("Downloading...")
    # Process events to allow screen to refresh
    slicer.app.processEvents()

def onURLReceived(urlString):
    """Process DICOM view requests. Example:
    urlString="slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd"
    """
    url = qt.QUrl(urlString)
    if url.authority().lower() != "viewer":
        return
    query = qt.QUrlQuery(url)
    queryMap = {}
    for key, value in query.queryItems(qt.QUrl.FullyDecoded):
        queryMap[key] = qt.QUrl.fromPercentEncoding(value)
    if not "download" in queryMap:
        return
    downloadUrl = qt.QUrl(queryMap["download"])
    nodeName, ext = os.path.splitext(os.path.basename(downloadUrl.path()))
    import uuid
    # Generate random filename to avoid reusing/overwriting older downloaded files that may have the same name
    filename = f"{nodeName}-{uuid.uuid4().hex}{ext}"
    print(filename)
    try:
        progressWindow = slicer.util.createProgressDialog()
        import SampleData
        sampleDataLogic = SampleData.SampleDataLogic()
        sampleDataLogic.progressWindow = progressWindow
        sampleDataLogic.logMessage = lambda message, level=None, sampleDataLogic=sampleDataLogic: reportProgress(message, level, sampleDataLogic)
        loadedNodes = sampleDataLogic.downloadFromURL(nodeNames=nodeName, fileNames=filename, uris=downloadUrl.toString())
    finally:
        progressWindow.close()

To test, run this in the Python console (to download this sample nrrd file: https://github.com/rbumm/SlicerLungCTAnalyzer/releases/download/SampleData/LungCTAnalyzerChestCT.nrrd)

onURLReceived("slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd")

You can of course tune the URL format, add checksum (SampleData module can automatically cache downloaded data sets to avoid repeated downloads if the checksum value matches), etc.

Thanks for the reply. Some parts are not clear to me as I’m new to 3D slicer and python. Appreaciate if you could help me bit more.

Firstly, I followed “Creating Extensions” section in the below page to create an extension/module named “loadRemoteFile”:
https://www.slicer.org/wiki/Documentation/Nightly/Developers/ExtensionWizard

I added slicer.app.connect("urlReceived(QString)", self.onURLReceived) line and the script you mentioned above in loadRemoteFile.py. Then loaded the module from “Module finder” (It should be loaded automatically anyway as I checked ‘Add selected module to search paths’ when creaed extension). No error occurs at this point. However, if I run "onURLReceived(“slicer…” in Python interactor, get an error “NameError: name ‘onURLReceived’ is not defined”. What am I missing?

Also, I would like to know the way to pass the module I created to somebody. Give the whole contents of loadRemoteFile folder then users load it from Extension wizard->Select extension?

onURLReceived function is not found in the Python console if you don’t copy it into the Python console.

To test your module, you can launch Slicer.exe with the URL as the only command-line argument:

Slicer.exe slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd

To test if the custom URL association is set up correctly, you can open a command terminal in Windows and execute this:

start slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd`

Probably the easiest is to copy your module files to the lib\Slicer-4.13\qt-scripted-modules folder in the Slicer installation.

mmm… I’m still struggling. 3D slicer won’t complain anything if open up normally, however open up with “Slicer.exe slicer://…” or “start slicer://…” gets “Missing dicomweb_endpoint” error. Sounds like the module is not loaded correctly.

DICOM module received URL: slicer://viewer/?download=https%3A%2F%2Fgithub.com%2Frbumm%2FSlicerLungCTAnalyzer%2Freleases%2Fdownload%2FSampleData%2FLungCTAnalyzerChestCT.nrrd
Missing dicomweb_endpoint

The app complains if I insert something meaningless text in loadRemoteFile.py intentionally, so apparently the app is reading the py file.
I share the py file below. Could you check if anything wrong please?
I commented out slicer.app.connect("startupCompleted()", registerSampleData) at line 35.
“slicer.app.connect” is in line 36 followed by reportProgress and onURLReceived functions.

I also tried to put “slicer.app.connect()”, “reportProgress()” and “onURLReceived()” functions just under the “def setup(self):” line like the example you mentioned - still same error.

You have forgot to add the self argument to the reporting function when you made it a class member. I’ve made a few simplifications and uploaded a complete working example here:

It is working finally! Thank you so much for your support lassoan!

Steps:
Put LoadRemoteFile.py in lib\Slicer-4.13\qt-scripted-modules folder
Load module from module finder
Open 3d slicer from slice:// link on browser

I noticed the error “Missing dicomweb_endpoint”, but guess it’s safe to ignore.

1 Like

Yes that’s logged by the DICOM module, as it notices that the application is started with a slicer:// URL but the link does not point to a DICOMweb server. It is not an actual error, it is safe to ignore it. I’ll change it to a debug-level message.

1 Like