| 
          """ | 
        
        
           | 
          To  | 
        
        
           | 
           1) upload DICOM (.dcm) data of a patient and  | 
        
        
           | 
           2) perform auto-contouring on it and | 
        
        
           | 
           3) download the contours | 
        
        
           | 
          
 | 
        
        
           | 
          Tested with Raystation1-B and python3.6 | 
        
        
           | 
          
 | 
        
        
           | 
          Note: The MICCAI2015 dataset only has 1 planning scan/patient | 
        
        
           | 
          """ | 
        
        
           | 
          
 | 
        
        
           | 
          # Import private libs | 
        
        
           | 
          import connect | 
        
        
           | 
          
 | 
        
        
           | 
          # Import public libs | 
        
        
           | 
          import traceback | 
        
        
           | 
          from pathlib import Path | 
        
        
           | 
          
 | 
        
        
           | 
          ############################################################################### | 
        
        
           | 
          #                                   UTILS                                     # | 
        
        
           | 
          ############################################################################### | 
        
        
           | 
          
 | 
        
        
           | 
          def raystation_setup(): | 
        
        
           | 
          
 | 
        
        
           | 
              try: | 
        
        
           | 
                  patient = connect.get_current("Patient") | 
        
        
           | 
                  patient.Save() # need to do this so to avoid "PreConditionViolationException: State must be saved." | 
        
        
           | 
                  # case = patient.Cases # [0].TreatmentPlans | 
        
        
           | 
              except: | 
        
        
           | 
                  pass # if there is no patient open | 
        
        
           | 
          
 | 
        
        
           | 
          def vol_to_dicom_for_ct(path_img_ct, patient_name, patient_id, path_dicom): | 
        
        
           | 
          
 | 
        
        
           | 
              """ | 
        
        
           | 
              Converts a .nrrd/.mha/.nifti file into its .dcm files | 
        
        
           | 
          
 | 
        
        
           | 
              Params | 
        
        
           | 
              ------ | 
        
        
           | 
              path_img_ct: str, the path of the .nrrd/.mha/.nifti file | 
        
        
           | 
              patient_name: str | 
        
        
           | 
              patient_id: str | 
        
        
           | 
              path_dicom: str, the final output directory | 
        
        
           | 
               | 
        
        
           | 
              Note: Verify the output with dciodvfy | 
        
        
           | 
                  - Ref 1: https://www.dclunie.com/dicom3tools/workinprogress/index.html | 
        
        
           | 
                  - Ref 2: https://manpages.debian.org/unstable/dicom3tools/dciodvfy.1.en.html | 
        
        
           | 
                  - Ref 3: # Motivation: https://stackoverflow.com/questions/14350675/create-pydicom-file-from-numpy-array | 
        
        
           | 
              """ | 
        
        
           | 
          
 | 
        
        
           | 
              study_uid = None | 
        
        
           | 
              series_uid = None | 
        
        
           | 
          
 | 
        
        
           | 
              try: | 
        
        
           | 
                   | 
        
        
           | 
                  import sys | 
        
        
           | 
                  import copy | 
        
        
           | 
                  import random | 
        
        
           | 
                  import shutil | 
        
        
           | 
                  import subprocess | 
        
        
           | 
                  import numpy as np | 
        
        
           | 
          
 | 
        
        
           | 
                  if Path(path_img_ct).exists(): | 
        
        
           | 
                       | 
        
        
           | 
                      try: | 
        
        
           | 
                          import pydicom | 
        
        
           | 
                          import pydicom._storage_sopclass_uids | 
        
        
           | 
                      except: | 
        
        
           | 
                          subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--user', 'pydicom']) | 
        
        
           | 
                          import pydicom   | 
        
        
           | 
          
 | 
        
        
           | 
                      try: | 
        
        
           | 
                          import SimpleITK as sitk | 
        
        
           | 
                      except: | 
        
        
           | 
                          subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--user', 'SimpleITK']) # 2.1.1 | 
        
        
           | 
                          import SimpleITK as sitk | 
        
        
           | 
                       | 
        
        
           | 
                      try: | 
        
        
           | 
                          import matplotlib.pyplot as plt | 
        
        
           | 
                      except: | 
        
        
           | 
                          subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--user', 'matplotlib']) # 2.1.1 | 
        
        
           | 
                          import matplotlib.pyplot as plt | 
        
        
           | 
          
 | 
        
        
           | 
                      # Step 0 - Create save directory | 
        
        
           | 
                      if Path(path_dicom).exists(): | 
        
        
           | 
                          shutil.rmtree(path_dicom) | 
        
        
           | 
                      Path(path_dicom).mkdir(exist_ok=True, parents=True) | 
        
        
           | 
                       | 
        
        
           | 
                      # Step 1 - Get volume params | 
        
        
           | 
                      img_ct      = sitk.ReadImage(str(path_img_ct)) | 
        
        
           | 
                      img_spacing = tuple(img_ct.GetSpacing()) | 
        
        
           | 
                      img_origin  = tuple(img_ct.GetOrigin()) # --> dicom.ImagePositionPatient | 
        
        
           | 
                      img_array   = sitk.GetArrayFromImage(img_ct).astype(np.int16) # [D,H,W] | 
        
        
           | 
          
 | 
        
        
           | 
                      # Step 2 - Create dicom dataset | 
        
        
           | 
                      ds                     = pydicom.dataset.Dataset() | 
        
        
           | 
                      ds.FrameOfReferenceUID = pydicom.uid.generate_uid() # this will stay the same for all .dcm files of a volume | 
        
        
           | 
                       | 
        
        
           | 
                      # Step 2.1 - Modality details | 
        
        
           | 
                      ds.SOPClassUID         = pydicom._storage_sopclass_uids.CTImageStorage | 
        
        
           | 
                      ds.Modality            = 'CT' | 
        
        
           | 
                      ds.ImageType           = ['ORIGINAL', 'PRIMARY', 'AXIAL'] | 
        
        
           | 
          
 | 
        
        
           | 
                      # Step 2.2 - Image Details | 
        
        
           | 
                      ds.PixelSpacing               = [float(img_spacing[0]), float(img_spacing[1])] | 
        
        
           | 
                      ds.SliceThickness             = str(img_spacing[-1]) | 
        
        
           | 
                      ds.Rows                       = img_array.shape[1] | 
        
        
           | 
                      ds.Columns                    = img_array.shape[2] | 
        
        
           | 
                       | 
        
        
           | 
                      ds.PatientPosition            = 'HFS' | 
        
        
           | 
                      ds.ImageOrientationPatient    = [1, 0, 0, 0, 1, 0] | 
        
        
           | 
                      ds.PositionReferenceIndicator = 'SN' | 
        
        
           | 
                       | 
        
        
           | 
                      ds.SamplesPerPixel            = 1 | 
        
        
           | 
                      ds.PhotometricInterpretation  = 'MONOCHROME2' | 
        
        
           | 
                      ds.BitsAllocated              = 16 | 
        
        
           | 
                      ds.BitsStored                 = 16 | 
        
        
           | 
                      ds.HighBit                    = 15 | 
        
        
           | 
                      ds.PixelRepresentation        = 1 | 
        
        
           | 
                       | 
        
        
           | 
                      ds.RescaleIntercept           = "0.0" | 
        
        
           | 
                      ds.RescaleSlope               = "1.0" | 
        
        
           | 
                      ds.RescaleType                = 'HU' | 
        
        
           | 
                       | 
        
        
           | 
                      # Step 3.1 - Metadata | 
        
        
           | 
                      fileMeta = pydicom.Dataset() | 
        
        
           | 
                      fileMeta.MediaStorageSOPClassUID = pydicom._storage_sopclass_uids.CTImageStorage | 
        
        
           | 
                      fileMeta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid() # this will change for each .dcm file of a volume | 
        
        
           | 
                      fileMeta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian | 
        
        
           | 
                      ds.file_meta = fileMeta | 
        
        
           | 
          
 | 
        
        
           | 
                      # Step 3.2 - Include study details | 
        
        
           | 
                      ds.StudyInstanceUID    = pydicom.uid.generate_uid() | 
        
        
           | 
                      ds.StudyDescription    = '' | 
        
        
           | 
                      ds.StudyDate           = '19000101'                   # needed to create DICOMDIR | 
        
        
           | 
                      ds.StudyID             = str(random.randint(0,1000))  # needed to create DICOMDIR | 
        
        
           | 
          
 | 
        
        
           | 
                      # Step 3.3 - Include series details | 
        
        
           | 
                      ds.SeriesInstanceUID   = pydicom.uid.generate_uid() | 
        
        
           | 
                      ds.SeriesDescription   = '' | 
        
        
           | 
                      ds.SeriesNumber        = str(random.randint(0,1000)) # needed to create DICOMDIR | 
        
        
           | 
          
 | 
        
        
           | 
                      # Step 3.4 - Include patient details | 
        
        
           | 
                      ds.PatientName      = patient_name | 
        
        
           | 
                      ds.PatientID        = patient_id | 
        
        
           | 
          
 | 
        
        
           | 
                      # Step 3.5 - Manufacturer details | 
        
        
           | 
                      ds.Manufacturer           = 'MICCAI2015' | 
        
        
           | 
                      ds.ReferringPhysicianName = 'Mody'                   # needed for identification in RayStation | 
        
        
           | 
                      ds.ManufacturerModelName  = 'test_offsite' | 
        
        
           | 
          
 | 
        
        
           | 
                      # Step 4 - Make slices | 
        
        
           | 
                      for slice_id in range(img_array.shape[0]): | 
        
        
           | 
                           | 
        
        
           | 
                          # Step 4.1 - Slice identifier | 
        
        
           | 
                          random_uuid = pydicom.uid.generate_uid() | 
        
        
           | 
                          ds.file_meta.MediaStorageSOPInstanceUID = random_uuid | 
        
        
           | 
                          ds.SOPInstanceUID = random_uuid | 
        
        
           | 
                          ds.InstanceNumber = str(slice_id+1) | 
        
        
           | 
                           | 
        
        
           | 
                          vol_origin_tmp          = list(copy.deepcopy(img_origin)) | 
        
        
           | 
                          vol_origin_tmp[-1]      += img_spacing[-1]*slice_id | 
        
        
           | 
                          ds.ImagePositionPatient = vol_origin_tmp | 
        
        
           | 
          
 | 
        
        
           | 
                          # Step 4.2 - Slice data | 
        
        
           | 
                          img_slice               = img_array[slice_id,:,:] | 
        
        
           | 
                          # plt.imshow(img_slice); plt.savefig(str(Path(path_dicom, '{}.png'.format(slice_id)))); plt.close() | 
        
        
           | 
                          ds.PixelData            = img_slice.tobytes() | 
        
        
           | 
          
 | 
        
        
           | 
                          save_path               = Path(path_dicom).joinpath(str(ds.file_meta.MediaStorageSOPInstanceUID) + '.dcm') | 
        
        
           | 
                          ds.save_as(str(save_path), write_like_original=False) | 
        
        
           | 
          
 | 
        
        
           | 
                      study_uid = ds.StudyInstanceUID | 
        
        
           | 
                      series_uid = ds.SeriesInstanceUID | 
        
        
           | 
          
 | 
        
        
           | 
                  else: | 
        
        
           | 
                      print (' - [ERROR][vol_to_dicom_for_ct()] Error in path: path_img_ct: ', path_img_ct) | 
        
        
           | 
                       | 
        
        
           | 
              except: | 
        
        
           | 
                  traceback.print_exc() | 
        
        
           | 
               | 
        
        
           | 
              return study_uid, series_uid | 
        
        
           | 
          
 | 
        
        
           | 
          def raystation_upload_predict_download(path_dcm_patient_ct, patient_id, study_uid, series_uid, ): | 
        
        
           | 
          
 | 
        
        
           | 
              """ | 
        
        
           | 
              Params | 
        
        
           | 
              ------ | 
        
        
           | 
              path_dcm_patient_ct: Path to folder contains CT dicoms  | 
        
        
           | 
              """ | 
        
        
           | 
          
 | 
        
        
           | 
              try: | 
        
        
           | 
          
 | 
        
        
           | 
                  # Step 1 - Setup | 
        
        
           | 
                  db = connect.get_current('PatientDB') | 
        
        
           | 
          
 | 
        
        
           | 
                  # Step 2 - Upload data | 
        
        
           | 
                  if Path(path_dcm_patient_ct).exists(): | 
        
        
           | 
                      # Note: If the patient exists, this will still upload it and name it with a suffix | 
        
        
           | 
                      warnings = db.ImportPatientFromPath(Path=str(path_dcm_patient_ct), SeriesOrInstances=[{'PatientID': patient_id, 'StudyInstanceUID': str(study_uid), 'SeriesInstanceUID': str(series_uid)}], ImportFilter='', BrachyPlanImportOverrides={}) | 
        
        
           | 
                      patient = connect.get_current("Patient") | 
        
        
           | 
                      patient.Save() | 
        
        
           | 
          
 | 
        
        
           | 
                      # Step 3 - Perform auto-contouring | 
        
        
           | 
                      examination = connect.get_current('Examination') | 
        
        
           | 
                      examination.RunOarSegmentation(ModelName="RSL Head and Neck CT", ExaminationsAndRegistrations={ 'CT 1': None }, RoisToInclude=["Brainstem", "Bone_Mandible", "OpticNrv_L", "OpticNrv_R", "Parotid_L", "Parotid_R", "Glnd_Submand_L", "Glnd_Submand_R", "Joint_TM_L", "Joint_TM_R"]) | 
        
        
           | 
                       | 
        
        
           | 
                      # Step 4 - Download data | 
        
        
           | 
                      patient.Save() | 
        
        
           | 
                      case        = connect.get_current('Case') | 
        
        
           | 
                      examination = connect.get_current('Examination') | 
        
        
           | 
          
 | 
        
        
           | 
                      path_dcm_patient_rs = str(path_dcm_patient_ct) + '-RSAutoContour' | 
        
        
           | 
                      Path(path_dcm_patient_rs).mkdir(exist_ok=True) | 
        
        
           | 
                      case.ScriptableDicomExport(ExportFolderPath = str(path_dcm_patient_rs), AnonymizationSettings={"Anonymize": False}, RtStructureSetsForExaminations = [examination.Name], IgnorePreConditionWarnings=False) # , Examinations = [examination.Name] | 
        
        
           | 
                   | 
        
        
           | 
                  else: | 
        
        
           | 
                      print (' - [ERROR][raystation_upload_predict_download()] Issues with path: path_dcm_patient_ct: ', path_dcm_patient_ct) | 
        
        
           | 
                                                   | 
        
        
           | 
              except: | 
        
        
           | 
                  traceback.print_exc() | 
        
        
           | 
          
 | 
        
        
           | 
          def download_purepython_package(url_release, folderpath_package): | 
        
        
           | 
              """ | 
        
        
           | 
              We can directly download and use this since it is a pure python package | 
        
        
           | 
              Defunct in RS as you just pip install using sys.executable | 
        
        
           | 
              """ | 
        
        
           | 
              import importlib | 
        
        
           | 
              import urllib | 
        
        
           | 
              import zipfile | 
        
        
           | 
              import urllib.request | 
        
        
           | 
          
 | 
        
        
           | 
              def download_zip(url_zip, filepath_zip, filepath_output): | 
        
        
           | 
                  urllib.request.urlretrieve(url_zip, filename=filepath_zip) | 
        
        
           | 
                  read_zip(filepath_zip, filepath_output) | 
        
        
           | 
          
 | 
        
        
           | 
              def read_zip(filepath_zip, filepath_output=None, leave=False): | 
        
        
           | 
                   | 
        
        
           | 
                  # Step 0 - Init | 
        
        
           | 
                  if Path(filepath_zip).exists(): | 
        
        
           | 
                      if filepath_output is None: | 
        
        
           | 
                          filepath_zip_parts     = list(Path(filepath_zip).parts) | 
        
        
           | 
                          filepath_zip_name      = filepath_zip_parts[-1].split('.zip')[0] | 
        
        
           | 
                          filepath_zip_parts[-1] = filepath_zip_name | 
        
        
           | 
                          filepath_output        = Path(*filepath_zip_parts) | 
        
        
           | 
          
 | 
        
        
           | 
                      zip_fp = zipfile.ZipFile(filepath_zip, 'r') | 
        
        
           | 
                      zip_fp_members = zip_fp.namelist() | 
        
        
           | 
                      for member in zip_fp_members: | 
        
        
           | 
                          zip_fp.extract(member, filepath_output) | 
        
        
           | 
                               | 
        
        
           | 
                      return filepath_output | 
        
        
           | 
                  else: | 
        
        
           | 
                      print (' - [ERROR][read_zip()] Path does not exist: ', filepath_zip) | 
        
        
           | 
                      return None | 
        
        
           | 
          
 | 
        
        
           | 
          
 | 
        
        
           | 
              # Step 0.1 - Init | 
        
        
           | 
              module_name = Path(folderpath_package).parts[-1] | 
        
        
           | 
              filepath_zip = str(folderpath_package) + '.zip' | 
        
        
           | 
              folderpath_output = str(folderpath_package) + '-download' | 
        
        
           | 
               | 
        
        
           | 
              # Step 0.2 - Clear previous content | 
        
        
           | 
              if Path(filepath_zip).exists(): | 
        
        
           | 
                  Path(filepath_zip).unlink() | 
        
        
           | 
              if Path(folderpath_output).exists(): | 
        
        
           | 
                  shutil.rmtree(str(folderpath_output)) | 
        
        
           | 
              if Path(folderpath_package).exists(): | 
        
        
           | 
                  shutil.rmtree(str(folderpath_package))     | 
        
        
           | 
          
 | 
        
        
           | 
              # Step 1 - Download release | 
        
        
           | 
              download_zip(url_release, filepath_zip, folderpath_output) | 
        
        
           | 
              Path(filepath_zip).unlink() | 
        
        
           | 
          
 | 
        
        
           | 
              # Step 2 - PMove around stuff | 
        
        
           | 
              folderpath_tmp = [path for path in Path(folderpath_output).iterdir()][0] | 
        
        
           | 
              src = str(Path(folderpath_tmp, module_name)) | 
        
        
           | 
              dst = str(Path(folderpath_package)) | 
        
        
           | 
              shutil.copytree(src, dst) | 
        
        
           | 
              shutil.rmtree(str(folderpath_output)) | 
        
        
           | 
          
 | 
        
        
           | 
              sys.path.append(str(Path(folderpath_output).parent.absolute())) | 
        
        
           | 
              importlib.import_module(module_name) | 
        
        
           | 
          
 | 
        
        
           | 
              # url_release_pydicom = 'https://github.com/pydicom/pydicom/archive/refs/tags/v2.3.1.zip' | 
        
        
           | 
              # folderpath_package_pydicom = Path(DIR_FILE).joinpath('pydicom') | 
        
        
           | 
              # download_purepython_package(url_release_pydicom, folderpath_package_pydicom) | 
        
        
           | 
              # import pydicom | 
        
        
           | 
              pass | 
        
        
           | 
          
 | 
        
        
           | 
          ############################################################################### | 
        
        
           | 
          #                                   MAIN                                      # | 
        
        
           | 
          ############################################################################### | 
        
        
           | 
          
 | 
        
        
           | 
          if __name__ == "__main__": | 
        
        
           | 
              """ | 
        
        
           | 
              Implement your custom dataset loop here! | 
        
        
           | 
              """ | 
        
        
           | 
              raystation_setup() | 
        
        
           | 
          
 | 
        
        
           | 
              if 1: | 
        
        
           | 
          
 | 
        
        
           | 
                  print ('\n ======================================================== ') | 
        
        
           | 
                  print ('=                   MICCAI 2015 (test_offsite)             =') | 
        
        
           | 
                  print (' ======================================================== \n') | 
        
        
           | 
          
 | 
        
        
           | 
                  # Step 0 - Init | 
        
        
           | 
                  DIR_FILE            = Path(__file__).parent.absolute() | 
        
        
           | 
                  DIR_DATA_MICOFFSITE = Path('H:\\').joinpath('RayStationData', 'MICCAI2015', 'test_offsite') | 
        
        
           | 
                  assert Path(DIR_DATA_MICOFFSITE).exists() == True | 
        
        
           | 
          
 | 
        
        
           | 
                  # Step 1 - Loop over patients | 
        
        
           | 
                  for patient_count, path_patient in enumerate(Path(DIR_DATA_MICOFFSITE).iterdir()): | 
        
        
           | 
          
 | 
        
        
           | 
                      try: | 
        
        
           | 
                          patient_name = Path(path_patient).parts[-1] | 
        
        
           | 
                          patient_id   = 'MICCAI2015-Test-' + patient_name | 
        
        
           | 
          
 | 
        
        
           | 
                          print ('\n\n ----------------------------------------- Patient: ', patient_id) | 
        
        
           | 
                          path_img = Path(path_patient).joinpath('img_resampled_{}.nrrd'.format(patient_name)) | 
        
        
           | 
                          path_img_dicom = Path(path_patient).joinpath('img_resampled_{}_dcm'.format(patient_name)) | 
        
        
           | 
                           | 
        
        
           | 
                          # Step 2 - Dicomize | 
        
        
           | 
                          study_uid, series_uid = vol_to_dicom_for_ct(path_img_ct=path_img, patient_name=patient_name, patient_id=patient_id, path_dicom=path_img_dicom) | 
        
        
           | 
                           | 
        
        
           | 
                          # Step 3 - Predict OARs | 
        
        
           | 
                          if study_uid is not None and series_uid is not None: | 
        
        
           | 
                              raystation_upload_predict_download(path_img_dicom, patient_id=patient_id, study_uid=study_uid, series_uid=series_uid) | 
        
        
           | 
          
 | 
        
        
           | 
                          # break | 
        
        
           | 
                          # if patient_count > 2: | 
        
        
           | 
                          #     break | 
        
        
           | 
          
 | 
        
        
           | 
                      except: | 
        
        
           | 
                          traceback.print_exc() |