Automatically uploading assessors

259 views
Skip to first unread message

Michael Berks

unread,
Aug 31, 2022, 11:00:07 AM8/31/22
to xnat_discussion
Hi all

I'm a newbie user of XNAT trying to help my collaborators run processing pipelines through the XNAT container service.

So far I have figured out how to run generic processing pipelines in a docker image, accessing DICOM images for a given session and creating output image maps in the session resources folder. These can then be downloaded and eg viewed/analysed offline. 

What I'd like to do to complete the process is be able to automatically "upload" these images so they can be viewed alongside the original session images.

To given a concrete example: a user has a set of variable flip-angle MR images uploaded to an XNAT session with the aim of mapping baseline T1. T1 mapping is performed via the container service, generating baseline T1 and M0 maps as DICOM images in the session resources. We want to be able to add the T1 and M0 maps to the original session so they can be viewed alongside the input VFA images.

This process appears to be outlined in the following wiki post: 
https://wiki.xnat.org/container-service/automatically-uploading-assessors-122978873.html

However I can find no information on:
1. Who posted it
2. When
3. What, if any progress, has been made to make this service available to users

Can anyone shed any light on this? 

If not, has anyone made their own workarounds to perform the same task (eg manually creating their own XML assessor) and can give some tips on how to get started?

Many thanks in advance for your help,

Michael Berks
University of Manchester

Charlie Moore

unread,
Aug 31, 2022, 11:35:55 AM8/31/22
to xnat_discussion
Hi Michael,

Can you define a little more what you mean by "so they can be viewed alongside the original session images"? If you mean you want to be able to see them in the "Scans" list and/or view them as standalone DICOM images in the XNAT-OHIF viewer plugin, what you would want to do is create one or more new scans for them, rather than an assessor. For creating a scan in Container Service, this post may help get you started https://groups.google.com/g/xnat_discussion/c/iv7oPXYb0vw/m/1wU9_0qGCwAJ. The caveat about needing custom CS/XNAT code can safely be ignored as it has since been released in the main line. I will add as a caveat however that if you intend to view your scans in the OHIF viewer, you will likely need to set a good amount of metadata on the scan object via its XML that the normal DICOM importer does automatically. Hopefully that helps.

Thanks,
Charlie

John Flavin

unread,
Aug 31, 2022, 11:54:33 AM8/31/22
to xnat_di...@googlegroups.com
Hi Michael,

Looking at the back end of that wiki page, I can see that it was created by me and last modified Dec. 11, 2017. So it's more than a bit out of date.

Since that time we have made progress on the plans documented in that page. You can automatically create an image assessor that gets created on a session. I don't know that we have a page that documents this in a lot of detail, which is an oversight on my part. Sorry about that. The container service documentation has been difficult to keep up with.

A sketch of how uploading an assessor works is that you need an assessor data type of some kind, your container would produce an XML file that matches that assessor data type's schema, your command would have an output pointing to the path where that file will be, and the command wrapper (the object in the "xnat" section in the command json) would have an output handler that accepts the command output and creates a new assessor as a child of the session.

That was a lot to absorb, I'm sure. Here is an example command json that does all these things: https://github.com/NrgXnat/docker-images/blob/master/sample-qc-assessor/command.json. The actual container that this command runs isn't useful; it makes a manual QC assessor filled with random data. But the command's structure could be helpful.

However, it seems to me that an assessor may not be what you actually want. Let me return to what you said:

To given a concrete example: a user has a set of variable flip-angle MR images uploaded to an XNAT session with the aim of mapping baseline T1. T1 mapping is performed via the container service, generating baseline T1 and M0 maps as DICOM images in the session resources. We want to be able to add the T1 and M0 maps to the original session so they can be viewed alongside the input VFA images.(Emphasis mine)

This sounds to me like you want to create new scan resources to hold your container's output. The command to do that would be similar to the command I outlined above, except instead of producing an XML file and handling it by creating an assessor, your container would produce the image files and your command wrapper would handle the outputs by producing scan resources. An example to follow for that is the old standby dicom to nifti: https://github.com/NrgXnat/docker-images/blob/master/dcm2niix/command.json

Your command would likely be more complex, as you would have multiple outputs + output handlers. And it sounds like you would need to have multiple derived inputs respectively matching the different types of scans, so that your output handlers would know where to upload the resources in the end. But I think that example should give you a starting point. 

Other wiki pages that could be helpful: 

Please let us know how it goes!

John Flavin
Backend Team Lead
He/Him



--
You received this message because you are subscribed to the Google Groups "xnat_discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to xnat_discussi...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/xnat_discussion/a97227bf-e1d8-4253-8dc2-14b2d95e5f9cn%40googlegroups.com.

Michael Berks

unread,
Sep 1, 2022, 11:44:45 AM9/1/22
to xnat_discussion
Dear John and Charlie

Thanks both for your fast replies.

Yes, as you both correctly surmised, I'd got slightly the wrong end of the stick in what I needed to do (assessor vs scan resources).

As Charlie stated "If you mean you want to be able to see them in the "Scans" list and/or view them as standalone DICOM images in the XNAT-OHIF viewer plugin, what you would want to do is create one or more new scans for them, rather than an assessor." is exactly what we're trying to achieve.

I'll have read through of the links you both sent and see how I get on.

Thanks again,

BW

Michael

Michael Berks

unread,
Sep 14, 2022, 11:52:01 AM9/14/22
to xnat_di...@googlegroups.com
Dear John and Charlie

Thanks again for your helpful links. I've had a chance to have a bit more of a play with this, but while I feel like I can see the outline of a solution, there's still aspects that are confusing me.

So, following the discussion here https://groups.google.com/g/xnat_discussion/c/iv7oPXYb0vw/m/1wU9_0qGCwAJ, and particularly Kate Alpert's post, the outline appears to be:

  • In the JSON command file, for each scan you want added define:
    • An output specifying the scan XML file
    • An output specifying the directory of DICOM images for the scan
    • Matched output handlers for the above, with the scan XML having type Scan as a child of the session, and the images having type Resource as a child of the scan output handler
  • The container is then responsible for generating the scan XML files and DICOM images in the correct locations on the container mounts so they are picked up by the outputs/output handlers
  • Some XNAT automagic occurs to create new XNAT objects within the session
And previously this required a bespoke version of the container service but now this is implemented in the main version (although the container docs still say the output handlers can only have type Resource I tried running with an output handler set to Scan it while it didn't complete, it did at least launch so using type Scan seems to parse ok).

Is that right so far?

If so, the bit that is confusing me is how to complete the scan XML file, and specifically what the container needs to complete and what the automagic in the last step will do. Looking in the filesystem of our test XNAT instance I pulled off the XML file for a series that had been uploaded from the desktop client (scan_1201_catalog.xml).

The general structure, and some of the field details are easy enough to generate from a script in the container. However there are certain fields that AFAICT can only be completed *after* the XNAT objects have been built, so I don't understand how the container can generate them.

Specifically on line 2, the DCMCatalog UID looks like an ID automatically generate by the import

Then the entry for each image slice contains (among other fields that are easy to complete):
  • ID (sample value from attached XML "1.3.46.670589.11.22117.5.0.1560.2009120213165950016-1201-6-195xh2d.dcm"
  • UID ( "1.3.46.670589.11.22117.5.0.6228.2009120213340110721" 
  • URI ( "1.3.46.670589.11.22117.5.0.1560.2009120213165950016-1201-6-195xh2d.dcm" 
  • createdEventId ( "29"  the same for all entries)
  • digest ( "81a93cb5c0d9f39d930d96ab5c7eb0fd" )
In each case ID and URI appear to be the same and are the filename of the image slice on the filesystem. If the DICOM files will keep their name as they are created by the container when they are imported to the session (as session resources do) this is fine. But if not how does the container know what the file names should be? Will the container output-handler automatically handle this?

UID I'm guessing is a unique identifier for the slice in the XNAT database - what should the container include here? Nothing? Include the UID field but set the value empty?

digest: I can't find documentation on what this is. The values look like hexidecimals, so I'm guessing a bytestring of some sort?  

createEventID from looking at multiple scan XML files, this appears to be shared across multiple series that are included in the same upload.

I'm trying to work through this with a bit of trial and error but if you could help explain things a bit more concretely, or point to documentation that does, that would be really helpful. In general, if you can point me to a resource that explains the details of exactly what happens under the hood when Scan objects are created that would be really helpful. I suspect my mental model of what is going on might not be correct.

Finally, all of the above assumes that the command JSON files require explicit, separate output handlers for each series added to the session. While this isn't an insurmountable problem it would be limiting in terms of the general solution we are trying to put together. Do you know if there is another way to achieve this, so a single output handler can process multiple series saved to a mount point in the container (eg in the same way the import tool allows)? 

Many thanks again for your help

Michael


You received this message because you are subscribed to a topic in the Google Groups "xnat_discussion" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/xnat_discussion/b2i9QSjxCO4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to xnat_discussi...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/xnat_discussion/efe4ae11-04fc-4b87-97df-f4df2a1d9eb1n%40googlegroups.com.
scan_1201_catalog.xml

Charlie Moore

unread,
Sep 14, 2022, 12:28:19 PM9/14/22
to xnat_discussion
Hi Michael,

My command writing experience is fairly limited, so I'll have to leave most of these questions to Flavin. There are a few things I can add that I suspect will be helpful:
  1. There's a sample container image that runs on a session and creates a new scan. You may find it useful as a starting point: https://hub.docker.com/r/xnat/generatescan . Sorry, I probably should have remembered that earlier.
  2. "UID" at the scan level in XNAT maps to (0020,000E) Series Instance UID. If you're going to view these scans in the OHIF viewer, that means you're going to need to adhere to DICOM's model rather closely, as I'm pretty sure that plugin requires it. As such, in the DICOM for your generated data:
    1. Patient-level attributes (such as Patient ID, Patient Name, etc.) should match the rest of your study
    2. Study-level attributes (such as Study ID, but critically Study Instance UID) should match the rest of your study
    3. Series Instance UID should be the same for all instances in a series you're generating, but unique from the other UIDs in your study.
For (2), XNAT is quite permissive if you add scans/metadata/etc. after the fact with a method other than a standard importer (i.e. manually constructing scans, using container service, etc.), so you probably wouldn't run into issues until you tried to launch the viewer and it bombed out because a bunch of DICOM requirements are being violated.

Thanks,
Charlie

John Flavin

unread,
Sep 14, 2022, 8:41:42 PM9/14/22
to xnat_di...@googlegroups.com
I think you're on the right track.

the bit that is confusing me is how to complete the scan XML file, and specifically what the container needs to complete and what the automagic in the last step will do.

Yes, generating the proper XML is definitely the most complex part.


Looking in the filesystem of our test XNAT instance I pulled off the XML file for a series that had been uploaded from the desktop client (scan_1201_catalog.xml).

That isn't actually what you will need to generate. Those XML files that you'll find in the archive file system are catalog files. What you will need to make is much simpler.

If you refer back to the previous post you linked to, Kate posted a sample_scan.xml. That's the kind of thing you will need to generate in your container. It looks like a lot, but honestly most of that will be exactly the same for yours. 

I don't know how to say this other than to just throw you in the deep end: you're going to have to look at the schema to figure out how to construct the XML. All the scan data types are in the xnat.xsd schema, which you can find at <your xnat url>/schemas/xnat.xsd

Most of what you will need to know is in the base xnat:imageScanData type (which you can find by searching for the line <xs:complexType name="imageScanData">). The <xs:sequence> contains all the values that you can put in. Anything with minOccurs="0" is optional, and feel free to leave it out as you're developing if you don't think you need it. But if you do include any values, the order they appear in your scan XML needs to be the same order as in the imageScanData's xs:sequence.

Lastly, you can't actually create a scan with type xnat:imageScanData. That's an abstract type. You'll have to make MR scans or PET scans, etc. Again, refer to Kate's example. It starts with xnat:MRScan, which yours will need to do as well if it's an MR scan.

I think the generatescan image that Charlie sent could be helpful. Unfortunately that is only the image, which is harder to use as an example than the source files would be. I don't know where the source files for that image live.

I do have an example of a command that creates an assessor XML, https://github.com/NrgXnat/docker-images/tree/master/sample-qc-assessor. You can see in that repo a script that generates an assessor XML using the lxml etree python library. (It's just a debug example so it fills in randomly generated data, but you could do a similar thing with real scan information.)

That's a start, but there is a lot more to do. Sorry! I'll try to keep helping you along the process, so do let us know how it goes.

John Flavin
Backend Team Lead
He/Him



Michael Berks

unread,
Sep 15, 2022, 10:12:17 AM9/15/22
to xnat_di...@googlegroups.com
Hi John

Thanks again for the speedy and super helpful response. This is starting to make more sense. I feel a bit silly re the sample XML file from Kate. I opened this on my office machine and the browser rendered what looked like a bunch of Java(Script?)-ish code in one of the XML fields. With our earlier discussion on previously needed custom code and vague memories of XNAT being written in Java, I'd wrongly concluded this was something Kate had written that somehow got executed at runtime. As a C++/python programmer this sent me into a mild panic and didn't look closer to try to properly make sense of it...

Having opened the XML file again on my laptop I can see this was actually just an artefact firefox had created and the actual XML is much more self-explanatory!

It's going to be a couple of weeks before I can try this again but I'll give it a bash and let you know how it goes. I'm doing all my dev in a public GitLab repo, so if it works I'll be sure to share a link.

The ultimate aim is to produce some bolierplate templates that "XNAT-ify" docker images. So if you have a docker image that can be run as a container that conforms to the general template:

- Takes a command with input args specifying the location of some DICOM images and an optional config file
- Produces output DICOMs in a specified location

Then you can just run the boilerplate to generate a new docker image, complete with integrated command labels, that can be imported to XNAT and run, with the output images being loaded as new scans to the parent sessions.

Our initial aims are for this to work for T1 mapping, ADC modelling and DCE modelling, but it should be generalizable to much wider use cases. It will likely include optional to/from NIFTI wrappers to make compatible with processing code not set-up to work with DICOM.

If something like this doesn't already exist I hope it will be of benefit to the wider XNAT community as well as serving our own needs. And if it does exist we should probably just be using that instead - but I'm guessing if it did and you knew about it you'd already have pointed me to it!

Thanks again,

Michael

John Flavin

unread,
Sep 16, 2022, 12:07:32 PM9/16/22
to xnat_di...@googlegroups.com
Michael,

That sounds great! I don't know if you're planning to attend the upcoming XNAT Workshop, but to my eyes this would make an ideal hackathon project.


John Flavin
Backend Team Lead
He/Him


Reply all
Reply to author
Forward
0 new messages