text: """A medical image analysis pipeline.
  
  The pipeline is used for brain tissue segmentation using a decision forest classifier.
  """
  import argparse
  import os
  import sys
  import timeit
  # import pdb
  
  import SimpleITK as sitk
  import sklearn.ensemble as sk_ensemble
  import numpy as np
  import pymia.data.conversion as conversion
  import pymia.evaluation.writer as writer
  
  try:
      import mialab.data.structure as structure
      import mialab.utilities.file_access_utilities as futil
      import mialab.utilities.pipeline_utilities as putil
  except ImportError:
      # Append the MIALab root directory to Python path
      sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), '..'))
      import mialab.data.structure as structure
      import mialab.utilities.file_access_utilities as futil
      import mialab.utilities.pipeline_utilities as putil
  
  LOADING_KEYS = [structure.BrainImageTypes.T1w,
                  structure.BrainImageTypes.T2w,
                  structure.BrainImageTypes.GroundTruth,
                  structure.BrainImageTypes.BrainMask,
                  structure.BrainImageTypes.RegistrationTransform]  # the list of data we will load
  
  
  def main(result_dir: str, data_atlas_dir: str, data_train_dir: str, data_test_dir: str, tree_n, tree_d, label_nbr):
      """Brain tissue segmentation using decision forests.
  
      The main routine executes the medical image analysis pipeline:
  
          - Image loading
          - Registration
          - Pre-processing
          - Feature extraction
          - Decision forest classifier model building
          - Segmentation using the decision forest classifier model on unseen images
          - Post-processing of the segmentation
          - Evaluation of the segmentation
      """
  
      # load atlas images
      putil.load_atlas_images(data_atlas_dir)
  
      print('-' * 5, 'Training...')
  
      # crawl the training image directories
      crawler = futil.FileSystemDataCrawler(data_train_dir,
                                            LOADING_KEYS,
                                            futil.BrainImageFilePathGenerator(),
                                            futil.DataDirectoryFilter())
      pre_process_params = {'skullstrip_pre': True,
                            'normalization_pre': True,
                            'registration_pre': True,
                            'coordinates_feature': True,
                            'intensity_feature': True,
                            'gradient_intensity_feature': True}
  
      # load images for training and pre-process
      images = putil.pre_process_batch(crawler.data, pre_process_params, multi_process=False)
  
      # generate feature matrix and label vector
      data_train = np.concatenate([img.feature_matrix[0] for img in images])
      labels_train = np.concatenate([img.feature_matrix[1] for img in images]).squeeze()
  
      # Ensure deep copy and set all labels apart of the searched one to zero (background)
      singleLabel = labels_train.copy()
      singleLabel[np.where(singleLabel != label_nbr)] = 0
  
      # warnings.warn('Random forest parameters not properly set.')
      forest = sk_ensemble.RandomForestClassifier(max_features=2,
                                                  n_estimators=tree_n,
                                                  max_depth=tree_d)
  
      start_time = timeit.default_timer()
      if label_nbr == 0:
          forest.fit(data_train, labels_train)
      else:
          forest.fit(data_train, singleLabel)
      print(' Time elapsed:', timeit.default_timer() - start_time, 's')
  
      # create a result directory
      folder_id = 'TreeD-' + str(tree_d).zfill(3) + '-TreeN-' + str(tree_n).zfill(3) + '-Label-' + putil.BrainLabels(label_nbr).name
      result_dir = os.path.join(result_dir, folder_id)
      os.makedirs(result_dir, exist_ok=True)
  
      print('-' * 5, 'Testing...')
  
      # initialize evaluator
      evaluator = putil.init_evaluator(label_nbr)
  
      # crawl the training image directories
      crawler = futil.FileSystemDataCrawler(data_test_dir,
                                            LOADING_KEYS,
                                            futil.BrainImageFilePathGenerator(),
                                            futil.DataDirectoryFilter())
  
      # load images for testing and pre-process
      pre_process_params['training'] = False
      images_test = putil.pre_process_batch(crawler.data, pre_process_params, multi_process=False)
  
      images_prediction = []
      images_probabilities = []
  
      for img in images_test:
          print('-' * 10, 'Testing', img.id_)
  
          start_time = timeit.default_timer()
          predictions = forest.predict(img.feature_matrix[0])
          probabilities = forest.predict_proba(img.feature_matrix[0])
          print(' Time elapsed:', timeit.default_timer() - start_time, 's')
  
          # convert prediction and probabilities back to SimpleITK images
          image_prediction = conversion.NumpySimpleITKImageBridge.convert(predictions.astype(np.uint8),
                                                                          img.image_properties)
          image_probabilities = conversion.NumpySimpleITKImageBridge.convert(probabilities, img.image_properties)
  
          # evaluate segmentation without post-processing
          evaluator.evaluate(image_prediction, img.images[structure.BrainImageTypes.GroundTruth], img.id_)
  
          images_prediction.append(image_prediction)
          images_probabilities.append(image_probabilities)
  
      # post-process segmentation and evaluate with post-processing
      post_process_params = {'simple_post': True}
      images_post_processed = putil.post_process_batch(images_test, images_prediction, images_probabilities,
                                                       post_process_params, multi_process=True)
  
      for i, img in enumerate(images_test):
          evaluator.evaluate(images_post_processed[i], img.images[structure.BrainImageTypes.GroundTruth],
                             img.id_ + '-PP')
  
          # save results
          sitk.WriteImage(images_prediction[i], os.path.join(result_dir, images_test[i].id_ + folder_id + '_SEG.mha'), True)
          sitk.WriteImage(images_post_processed[i], os.path.join(result_dir, images_test[i].id_ + folder_id + '_SEG.mha'), True)
  
      # use two writers to report the results
      os.makedirs(result_dir, exist_ok=True)  # generate result directory, if it does not exists
      result_file = os.path.join(result_dir, 'results.csv')
      writer.CSVWriter(result_file).write(evaluator.results)
  
      print('\nSubject-wise results...')
      writer.ConsoleWriter().write(evaluator.results)
  
      # report also mean and standard deviation among all subjects
      result_summary_file = os.path.join(result_dir, 'results_summary.csv')
      functions = {'MEAN': np.mean, 'STD': np.std}
      writer.CSVStatisticsWriter(result_summary_file, functions=functions).write(evaluator.results)
      print('\nAggregated statistic results...')
      writer.ConsoleStatisticsWriter(functions=functions).write(evaluator.results)
  
      # clear results such that the evaluator is ready for the next evaluation
      evaluator.clear()
  
  if __name__ == "__main__":
      """The program's entry point."""
  
      script_dir = os.path.dirname(sys.argv[0])
  
      parser = argparse.ArgumentParser(description='Medical image analysis pipeline for brain tissue segmentation')
  
      parser.add_argument(
          '--result_dir',
          type=str,
          default=os.path.normpath(os.path.join(script_dir, './mia-result')),
          help='Directory for results.'
      )
  
      parser.add_argument(
          '--data_atlas_dir',
          type=str,
          default=os.path.normpath(os.path.join(script_dir, '../data/atlas')),
          help='Directory with atlas data.'
      )
  
      parser.add_argument(
          '--data_train_dir',
          type=str,
          default=os.path.normpath(os.path.join(script_dir, '../data/train/')),
          help='Directory with training data.'
      )
  
      parser.add_argument(
          '--data_test_dir',
          type=str,
          default=os.path.normpath(os.path.join(script_dir, '../data/test/')),
          help='Directory with testing data.'
      )
  
      args = parser.parse_args()
  
      # Iterating settings
      tree_nbr = [1, 5, 10, 20, 50]
      tree_depths = [5, 10, 20, 40, 80]
  
      # Create results of multi or and single label, by default all numbers 0 - 5 are active
      # 0: Multi-label all structures in one step!
      # 1-5: Single-label: ['WhiteMatter', 'GreyMatter', 'Hippocampus', 'Amygdala', 'Thalamus']
      labels = [0, 1, 2, 3, 4, 5]
  
      for i in range(len(tree_nbr)):
          for ii in range(len(tree_depths)):
              for iii in range(len(labels)):
                  main(args.result_dir, args.data_atlas_dir, args.data_train_dir, args.data_test_dir,
                       tree_nbr[i], tree_depths[ii], labels[iii])
  