class_docstr: Prediction pipeline:
  - receives a patched patient image (n_patches, c, y, x, (z)) from patient data loader.
  - forwards patches through model in chunks of batch_size. (method: batch_tiling_forward)
  - unmolds predictions (boxes and segmentations) to original patient coordinates. (method: spatial_tiling_forward)
  
  Ensembling (mode == 'test'):
  - for inference, forwards 4 mirrored versions of image to through model and unmolds predictions afterwards
    accordingly (method: data_aug_forward)
  - for inference, loads multiple parameter-sets of the trained model corresponding to different epochs. for each
    parameter-set loops over entire test set, runs prediction pipeline for each patient. (method: predict_test_set)
  
  Consolidation of predictions:
  - consolidates a patient's predictions (boxes, segmentations) collected over patches, data_aug- and temporal ensembling,
    performs clustering and weighted averaging (external function: apply_wbc_to_patient) to obtain consistent outptus.
  - for 2D networks, consolidates box predictions to 3D cubes via clustering (adaption of non-maximum surpression).
    (external function: merge_2D_to_3D_preds_per_patient)
  
  Ground truth handling:
  - dissmisses any ground truth boxes returned by the model (happens in validation mode, patch-based groundtruth)
  - if provided by data loader, adds 3D ground truth to the final predictions to be passed to the evaluator.
class_docstr_tok: ["Prediction","pipeline",":","-","receives","a","patched","patient","image","(","n_patches",",","c",",","y",",","x",",","(","z",")",")","from","patient","data","loader",".","-","forwards","patches","through","model","in","chunks","of","batch_size",".","(","method",":","batch_tiling_forward",")","-","unmolds","predictions","(","boxes","and","segmentations",")","to","original","patient","coordinates",".","(","method",":","spatial_tiling_forward",")","Ensembling","(","mode","==","'test","'",")",":","-","for","inference",",","forwards","4","mirrored","versions","of","image","to","through","model","and","unmolds","predictions","afterwards","accordingly","(","method",":","data_aug_forward",")","-","for","inference",",","loads","multiple","parameter-sets","of","the","trained","model","corresponding","to","different","epochs",".","for","each","parameter-set","loops","over","entire","test","set",",","runs","prediction","pipeline","for","each","patient",".","(","method",":","predict_test_set",")","Consolidation","of","predictions",":","-","consolidates","a","patient","'s","predictions","(","boxes",",","segmentations",")","collected","over","patches",",","data_aug-","and","temporal","ensembling",",","performs","clustering","and","weighted","averaging","(","external","function",":","apply_wbc_to_patient",")","to","obtain","consistent","outptus",".","-","for","2D","networks",",","consolidates","box","predictions","to","3D","cubes","via","clustering","(","adaption","of","non-maximum","surpression",")",".","(","external","function",":","merge_2D_to_3D_preds_per_patient",")","Ground","truth","handling",":","-","dissmisses","any","ground","truth","boxes","returned","by","the","model","(","happens","in","validation","mode",",","patch-based","groundtruth",")","-","if","provided","by","data","loader",",","adds","3D","ground","truth","to","the","final","predictions","to","be","passed","to","the","evaluator","."]
docstr: forwards batch to batch_tiling_forward method and receives and returns a dictionary with results.
  if patch-based prediction, the results received from batch_tiling_forward will be on a per-patch-basis.
  this method uses the provided patch_crops to re-transform all predictions to whole-image coordinates.
  Patch-origin information of all box-predictions will be needed for consolidation, hence it is stored as
  'patch_id', which is a unique string for each patch (also takes current data aug and temporal epoch instances
  into account). all box predictions get additional information about the amount overlapping patches at the
  respective position (used for consolidation).
  :return. results_dict: stores the results for one patient. dictionary with keys:
           - 'boxes': list over batch elements. each element is a list over boxes, where each box is
                      one dictionary: [[box_0, ...], [box_n,...]]. batch elements are slices for 2D predictions,
                      and a dummy batch dimension of 1 for 3D predictions.
           - 'seg_preds': pixel-wise predictions. (b, 1, y, x, (z))
           - monitor_values (only in validation mode)
docstr_tok: ["forwards","batch","to","batch_tiling_forward","method","and","receives","and","returns","a","dictionary","with","results",".","if","patch-based","prediction",",","the","results","received","from","batch_tiling_forward","will","be","on","a","per-patch-basis",".","this","method","uses","the","provided","patch_crops","to","re-transform","all","predictions","to","whole-image","coordinates",".","Patch-origin","information","of","all","box-predictions","will","be","needed","for","consolidation",",","hence","it","is","stored","as","'patch_id","'",",","which","is","a","unique","string","for","each","patch","(","also","takes","current","data","aug","and","temporal","epoch","instances","into","account",")",".","all","box","predictions","get","additional","information","about","the","amount","overlapping","patches","at","the","respective","position","(","used","for","consolidation",")",".",":","return",".","results_dict",":","stores","the","results","for","one","patient",".","dictionary","with","keys",":","-","'boxes","'",":","list","over","batch","elements",".","each","element","is","a","list","over","boxes",",","where","each","box","is","one","dictionary",":","[","[","box_0",",","...","]",",","[","box_n",",","...","]","]",".","batch","elements","are","slices","for","2D","predictions",",","and","a","dummy","batch","dimension","of","1","for","3D","predictions",".","-","'seg_preds","'",":","pixel-wise","predictions",".","(","b",",","1",",","y",",","x",",","(","z",")",")","-","monitor_values","(","only","in","validation","mode",")"]
code: def spatial_tiling_forward(self, batch, patch_crops=None, n_aug="0"):
      """
      forwards batch to batch_tiling_forward method and receives and returns a dictionary with results.
      if patch-based prediction, the results received from batch_tiling_forward will be on a per-patch-basis.
      this method uses the provided patch_crops to re-transform all predictions to whole-image coordinates.
      Patch-origin information of all box-predictions will be needed for consolidation, hence it is stored as
      'patch_id', which is a unique string for each patch (also takes current data aug and temporal epoch instances
      into account). all box predictions get additional information about the amount overlapping patches at the
      respective position (used for consolidation).
      :return. results_dict: stores the results for one patient. dictionary with keys:
               - 'boxes': list over batch elements. each element is a list over boxes, where each box is
                          one dictionary: [[box_0, ...], [box_n,...]]. batch elements are slices for 2D predictions,
                          and a dummy batch dimension of 1 for 3D predictions.
               - 'seg_preds': pixel-wise predictions. (b, 1, y, x, (z))
               - monitor_values (only in validation mode)
      """
      if patch_crops is not None:
          patches_dict = self.batch_tiling_forward(batch)
  
          results_dict = {
              "boxes": [
                  [] for _ in range(batch["original_img_shape"][0])
              ]
          }
  
          # instanciate segemntation output array. Will contain averages over patch predictions.
          out_seg_preds = np.zeros(
              batch["original_img_shape"], dtype=np.float16
          )[:, 0][:, None]
          # counts patch instances per pixel-position.
          patch_overlap_map = np.zeros_like(
              out_seg_preds, dtype="uint8"
          )
  
          # unmold segmentation outputs. loop over patches.
          for pix, pc in enumerate(patch_crops):
              if self.cf.dim == 3:
                  out_seg_preds[
                      :, :, pc[0] : pc[1], pc[2] : pc[3], pc[4] : pc[5]
                  ] += patches_dict["seg_preds"][pix][None]
                  patch_overlap_map[
                      :, :, pc[0] : pc[1], pc[2] : pc[3], pc[4] : pc[5]
                  ] += 1
              else:
                  out_seg_preds[
                      pc[4] : pc[5],
                      :,
                      pc[0] : pc[1],
                      pc[2] : pc[3],
                  ] += patches_dict["seg_preds"][pix]
                  patch_overlap_map[
                      pc[4] : pc[5],
                      :,
                      pc[0] : pc[1],
                      pc[2] : pc[3],
                  ] += 1
  
          # take mean in overlapping areas.
          out_seg_preds[patch_overlap_map > 0] /= patch_overlap_map[
              patch_overlap_map > 0
          ]
          results_dict["seg_preds"] = out_seg_preds
  
          # unmold box outputs. loop over patches.
          for pix, pc in enumerate(patch_crops):
              patch_boxes = patches_dict["boxes"][pix]
  
              for box in patch_boxes:
                  # add unique patch id for consolidation of predictions.
                  box["patch_id"] = (
                      self.rank_ix + "_" + n_aug + "_" + str(pix)
                  )
  
                  # boxes from the edges of a patch have a lower prediction quality, than the ones at patch-centers.
                  # hence they will be downweighted for consolidation, using the 'box_patch_center_factor', which is
                  # obtained by a normal distribution over positions in the patch and average over spatial dimensions.
                  # Also the info 'box_n_overlaps' is stored for consolidation, which depicts the amount over
                  # overlapping patches at the box's position.
                  c = box["box_coords"]
                  box_centers = [
                      (c[ii] + c[ii + 2]) / 2 for ii in range(2)
                  ]
                  if self.cf.dim == 3:
                      box_centers.append((c[4] + c[5]) / 2)
                  box["box_patch_center_factor"] = np.mean(
                      [
                          norm.pdf(bc, loc=pc, scale=pc * 0.8)
                          * np.sqrt(2 * np.pi)
                          * pc
                          * 0.8
                          for bc, pc in zip(
                              box_centers,
                              np.array(self.cf.patch_size) / 2,
                          )
                      ]
                  )
                  if self.cf.dim == 3:
                      c += np.array(
                          [pc[0], pc[2], pc[0], pc[2], pc[4], pc[4]]
                      )
                      int_c = [
                          int(np.floor(ii))
                          if ix % 2 == 0
                          else int(np.ceil(ii))
                          for ix, ii in enumerate(c)
                      ]
                      box["box_n_overlaps"] = np.mean(
                          patch_overlap_map[
                              :,
                              :,
                              int_c[1] : int_c[3],
                              int_c[0] : int_c[2],
                              int_c[4] : int_c[5],
                          ]
                      )
                      results_dict["boxes"][0].append(box)
                  else:
                      c += np.array([pc[0], pc[2], pc[0], pc[2]])
                      int_c = [
                          int(np.floor(ii))
                          if ix % 2 == 0
                          else int(np.ceil(ii))
                          for ix, ii in enumerate(c)
                      ]
                      box["box_n_overlaps"] = np.mean(
                          patch_overlap_map[
                              pc[4],
                              :,
                              int_c[1] : int_c[3],
                              int_c[0] : int_c[2],
                          ]
                      )
                      results_dict["boxes"][pc[4]].append(box)
  
          if self.mode == "val":
              results_dict["monitor_values"] = patches_dict[
                  "monitor_values"
              ]
  
      # if predictions are not patch-based:
      # add patch-origin info to boxes (entire image is the same patch with overlap=1) and return results.
      else:
          results_dict = self.batch_tiling_forward(batch)
          for b in results_dict["boxes"]:
              for box in b:
                  box["box_patch_center_factor"] = 1
                  box["box_n_overlaps"] = 1
                  box["patch_id"] = self.rank_ix + "_" + n_aug
  
      return results_dict
  
imports: import os
  import numpy
  import torch
  from scipy.stats import norm
  from collections import OrderedDict
  from multiprocessing import Pool
  import pickle
  import pandas
label_desc: Use image node_id 6 for calling the Predictor obj's underlying member method code with example usage: obj.spatial_tiling_forward(batch, patch_crops, n_aug) and returns: results_dict