-
Notifications
You must be signed in to change notification settings - Fork 205
Deployment Setup in a Hospital setting using Xnat and Monai label
Please refer to xnat integration wiki for monai label set up with xnat to show how monai label can help in annotation, and do interactive infer. Here, we will leverage Xnat integration with monai label as a deployment solution to run AI on demand once a hospital generates an image. We can use something like orthanc to simulate your hospital
Deployment steps are shown below
Step | Hospital | Xnat | Monai Label |
---|---|---|---|
1.2 | Orthanc send dicom study to xnat | . | . |
2.1 | . | Xnat receives dicoms | . |
2.2 | . | dicoms are routed to a specific project | . |
2.3 | . | automatically triggers a curl call to monai label to run inference | . |
3.1 | . | . | Receives request, triggers inference |
3.2 | . | . | Monai label uses common file system to immediately run inference |
3.3 | . | . | Generates nifti segmenation mask |
4.1 | . | . | Converts inference nifti to dicom-seg |
4.2 | . | . | Creates Secondary capture of segmentation |
4.3 | . | . | Creates rectangles for center slices for xnat annotation |
4.4 | . | . | Creates pdf report with key images of tumors |
4.5 | . | . | Emails Pdf report to project PIs |
4.6 | . | Monai label push volume calucation to xnat forms | . |
5.1 | . | . | Send all formats to xnat and hospital |
5.2 | Receives dicom-seg | Receives dicom-seg | . |
This workflow required multiple changes:
- Creating a command for xnat using the container service.
- Modifying the batch infer code on monai label
- Changing you app to use the new batch infer code
- Setting up Xnat to auto trigger the workflow.
On the xnat side you need to have the container service working. You can then add a new command to use a basic curl command to monai label to run inference for the given image_id. This is the most tricky part. You can use the json file below as a starting point. You must change the the host ip in the file below.
{
"name": "curl4monailabel_study",
"label": "curl4monailabel_study",
"description": "Curl to monai label to run infer on full study",
"version": "1.0",
"schema-version": "1.0",
"image": "curlimages/curl:latest",
"type": "docker",
"command-line": "curl -X 'POST' $monailabel_host':'$monailabel_port'/batch/infer/'[monailabel_model]'?scope=study&run_sync=true' -H 'accept: application/json' -H 'Content-Type: application/json' -d '{\"image_ids\":[\"[PRJ_ID]/[SUBJ_ID]/[ACCS_ID]/[SCAN_ID]/[SESS_ID]\"] }' ",
"override-entrypoint": true,
"mounts": [],
"environment-variables": {
"monailabel_port": "8000",
"monailabel_host": "http://<<your host ip >>"
},
"ports": {},
"inputs": [
{
"name": "subject_id",
"description": "The DICOM series to be sent for inference ",
"type": "string",
"required": true,
"replacement-key": "[SUBJ_ID]",
"command-line-flag": "",
"command-line-separator": "",
"select-values": []
},
{
"name": "project_id",
"description": "The project id for the Dicom series.",
"type": "string",
"required": false,
"replacement-key": "[PRJ_ID]",
"command-line-flag": "",
"command-line-separator": "",
"select-values": []
},
{
"name": "session_id",
"description": "The session id for the DICOM.",
"type": "string",
"required": false,
"replacement-key": "[SESS_ID]",
"command-line-flag": "",
"command-line-separator": "",
"select-values": []
},
{
"name": "accession_id",
"description": "The accession id for the DICOM.",
"type": "string",
"required": false,
"replacement-key": "[ACCS_ID]",
"command-line-flag": "",
"command-line-separator": "",
"select-values": []
},
{
"name": "scan_id",
"description": "Scan number for the dicom",
"type": "string",
"required": true,
"replacement-key": "[SCAN_ID]",
"command-line-flag": "",
"command-line-separator": "",
"select-values": []
},
{
"name": "monailabel_model",
"description": "Model name for monai label to run",
"type": "string",
"required": true,
"replacement-key": "[monailabel_model]",
"command-line-flag": "",
"command-line-separator": "",
"select-values": []
}
],
"outputs": [],
"xnat": [
{
"name": "curl4monailabel_study",
"description": "Monai Label Study ",
"contexts": [
"xnat:imageScanData"
],
"external-inputs": [
{
"name": "scan",
"description": "Input scan",
"type": "Scan",
"required": true,
"load-children": false
}
],
"derived-inputs": [
{
"name": "session",
"type": "Session",
"required": true,
"load-children": true,
"derived-from-wrapper-input": "scan",
"multiple": false
},
{
"name": "project-id",
"type": "string",
"required": true,
"provides-value-for-command-input": "project_id",
"user-settable": false,
"load-children": true,
"derived-from-wrapper-input": "session",
"derived-from-xnat-object-property": "project-id",
"multiple": false
},
{
"name": "session-id",
"type": "string",
"required": true,
"provides-value-for-command-input": "session_id",
"user-settable": false,
"load-children": true,
"derived-from-wrapper-input": "session",
"derived-from-xnat-object-property": "label",
"multiple": false
},
{
"name": "accession-id",
"type": "string",
"required": true,
"provides-value-for-command-input": "accession_id",
"user-settable": false,
"load-children": true,
"derived-from-wrapper-input": "session",
"derived-from-xnat-object-property": "id",
"multiple": false
},
{
"name": "subject-id",
"type": "string",
"required": true,
"provides-value-for-command-input": "subject_id",
"user-settable": false,
"load-children": true,
"derived-from-wrapper-input": "session",
"derived-from-xnat-object-property": "subject-id",
"multiple": false
},
{
"name": "scan-resource",
"type": "Resource",
"matcher": "@.label == 'DICOM'",
"required": true,
"provides-files-for-command-mount": "scan-in",
"load-children": true,
"derived-from-wrapper-input": "scan",
"multiple": false
},
{
"name": "scan-id",
"type": "string",
"required": true,
"provides-value-for-command-input": "scan_id",
"user-settable": false,
"load-children": true,
"derived-from-wrapper-input": "scan",
"derived-from-xnat-object-property": "id",
"multiple": false
}
],
"output-handlers": []
}
],
"reserve-memory": 1000,
"container-labels": {},
"generic-resources": {},
"ulimits": {},
"secrets": []
}
I am using the batch infer end point so the dicom seg can be saved back to xnat. For this to work you need to modify the batch infer end point in your app or monai label itself as
### create new file in your app folder as lib/tasks/batch_infer.py
class MyBatchInferTask(BatchInferTask):
def __call__(self, request, datastore: Datastore, infer: Callable):
image_ids = request.get("image_ids", "")
if not image_ids: # normal as before
image_ids = sorted(self.get_images(request, datastore))
else: ## image_ids is already sent from xnat
request["result_extension"] = ".nii.gz"
request["multi_gpu"] = False
then use this new class as you batch infer, so you need to add these lines to your app main.py file
def init_batch_infer(self) -> Callable:
from lib.tasks.batch_infer import MyBatchInferTask
return MyBatchInferTask()
After you see this working you need to go to even service on xnat to add an event. on "scan created" for your project or all projects then set action to the command you just created above "curl for monai label dli"