MONAILabel
MONAILabel copied to clipboard
Xnat datastore: Push back dicom seg to xnat
Is your feature request related to a problem? Please describe. Xnat datastore is great. Works great. Missing feature is to implement the save method in the data store so that Admin can run batch inference on segmenation models to trigger inference, convert nifti to dicom seg then push it back to xnat
Describe the solution you'd like I have implemented these methods in the xnat.py by adding functions below
def __convert_Nifti_2_dcmSeg(self,series_dir,niiSegPath,modelName,labelNames)->str:
label_info =[]
for i,lb in enumerate(labelNames):
label_info.append(
{"modelName": modelName, "name": str(i+1)+'_'+lb , "description": "lb"+str(i+1)+'_'+ lb}
)
dcmSegFile=nifti_to_dicom_seg(series_dir=series_dir, label=niiSegPath,label_info=label_info)
logging.info(f" converted nifit to dicom seg --- at {dcmSegFile}")
return dcmSegFile
##############################################################################
def __getDcmSegUploadurl(self,image_id,aiaaModelName):
project, subject, experiment, scan = self._id_to_fields(image_id)
assessionID= experiment
aiaaDcmSegUploadurlRoot = "/xapi/roi/projects/" + project + '/sessions/' + assessionID + '/collections/'
url = "{}/xapi/roi/projects/{}/sessions/{}/collections/Pat{}_S{}_{}".format(
self.api_url,
quote_plus(project),
quote_plus(assessionID),
quote_plus(subject),
quote_plus(scan),
quote_plus(aiaaModelName),
)
return url
##############################################################################
def save_label(self, image_id: str, label_filename: str, label_tag: str, label_info: Dict[str, Any]) -> str:
aiaaModelName = label_info.get('model', "NoModel") # waiting on how to get the model name
labelNames=label_info.get('params',{}).get('label_names',{})
#### convert nii to dcm seg and upload to Xnat
series_dir=self._download_image(image_id)
tmpDcmSegpath = self.__convert_Nifti_2_dcmSeg(series_dir,label_filename, aiaaModelName, labelNames)
data = open(tmpDcmSegpath, "rb")
url=self.__getDcmSegUploadurl(image_id,aiaaModelName)
self._request_put(url,data)
logger.info(f"------------- in xnat upload with {url=} and tmp file {tmpDcmSegpath=}")
return image_id
##############################################################################
def _request_put(self, url,data):
logger.info(f" in upload dcm seg {url}")
headers = {'Content-Type': "application/octet-stream"}
params = {"overwrite": "true", "type": "SEG"}
response = self.xnat_session.put(url, params=params, data=data, headers =headers, allow_redirects=True) # ,verify=False,
if response.status_code != 200: # failed call
logger.error(f" post call error status_code= {response.status_code} text ={response.text}")
else:
logger.info(f" put completed {response.text}")
return response
Also minor modification to the nifti_to_dicom_seg function in monailabel/datastore/utils/convert.py
def nifti_to_dicom_seg(series_dir, label, label_info, file_ext="*", use_itk=True):
start = time.time()
label_np, meta_dict = LoadImage()(label)
unique_labels = np.unique(label_np.flatten()).astype(np.int)
unique_labels = unique_labels[unique_labels != 0]
### add model name and labels
info = label_info[0] if label_info and 0 < len(label_info) else {}
modelName = info.get("modelName", "AIName")
.....
template = {
"ContentCreatorName": "Reader1",
"ClinicalTrialSeriesID": "Session1",
"ClinicalTrialTimePointID": "1",
"SeriesDescription": modelName , <<<---- add model name
"SeriesNumber": "300",
"InstanceNumber": "1",
"segmentAttributes": [segment_attributes],
"ContentLabel": "SEGMENTATION",
"ContentDescription": "MONAI Label - Image segmentation",
"ClinicalTrialCoordinatingCenterName": "MONAI",
"BodyPartExamined": "",
}
Awesome.. please create a draft PR for these additional changes
Hi Aharouni,
How did you set up the XNAT Datastore? I assume your data is stored in the XNAT and you are calling --studies http://{xnat_url}:{port}/dicom-web ?
Unfortunately, Xnat doesn't support dicom web standard yet. That is why we created the xnat datastore which has some rest APIs to download dicoms if you have an xnat username/password.
I have xnat set up using their docker compose setup then pull in xnat-ohif plugin from using
wget -nc -q -P plugins/ https://api.bitbucket.org/2.0/repositories/icrimaginginformatics/ohif-viewer-xnat-plugin/downloads/ohif-viewer-3.4.1.jar --no-check-certificate
and place it in the plugins folder
To start monai label with xnat datastore using
export MONAI_LABEL_DATASTORE=xnat
export MONAI_LABEL_DATASTORE_USERNAME=<your xnat username default is admin>
export MONAI_LABEL_DATASTORE_PASSWORD=<your xnat password default is admin>
export MONAI_LABEL_DATASTORE_ASSET_PATH=<location of xnat data archive>
export MONAI_LABEL_DATASTORE_PROJECT=<optional >
export MONAI_LABEL_DATASTORE_CACHE_PATH=<optional local cache path ignored if MONAI_LABEL_DATASTORE_ASSET_PATH is set>
export MONAI_LABEL_DICOMWEB_CONVERT_TO_NIFTI=false
monailabel start_server --app apps/radiology --studies http://<HOST_NAME running xnat> --conf models segmentation_spleen
Hi Aharouni,
Are changes required for the
def init_datastore(self) -> Datastore
function in main.py?
Not sure what are you referring to. The xnat datastore works fine with the exports I pointed to above. I opened this issue to point out additionally functionality of triggering batch inference and pushing results back to Xnat.