text: """
  Algorithms to extract various features of interest for outlier detection methods.
  
  """
  
  from os import makedirs
  from os.path import exists as pexists, join as pjoin, splitext
  
  import numpy as np
  
  from visualqc import config as cfg
  from visualqc.utils import read_image, scale_0to1
  
  
  def t1_histogram_whole_scan(in_mri_path,
                              num_bins=cfg.num_bins_histogram_intensity_distribution):
      """
      Computes histogram over the intensity distribution over the entire scan,
      including brain, skull and background.
  
      Parameters
      ----------
  
      in_mri_path : str
          Path to an MRI scan readable by Nibabel
  
      num_bins : int
          number of bins to use to create histogram
  
      Returns
      -------
      hist : ndarray
          Array of prob. densities for intensity
  
      """
  
      img = read_image(in_mri_path)
      # scaled, and reshaped
      arr_0to1 = scale_0to1(img).flatten()
      # compute prob. density
      hist, _ = np.histogram(arr_0to1, bins=num_bins, density=True)
  
      return hist
  
  
  def extract_T1_features(wf, feature_type='histogram_whole_scan'):
      """
      Returns a set of features from T1 sMRI scan from each subject.
  
      Parameters
      ----------
      wf : QCWorkFlow
          Self-contained object describing the details of a particular QC operation.
  
      feature_type : str
          String the identifying the type of features to read.
  
      Returns
      -------
      feature_paths : dict
          Dict containing paths to files with extracted features.
  
      """
  
      feature_type = feature_type.lower()
      basename = lambda name: splitext(name)[0]
      if wf.mri_name is not None:
          prefix = basename(wf.mri_name)+'_'
      else:
          prefix = ''
      out_csv_name = '{}{}_features.csv'.format(prefix, feature_type)
  
      feat_dir = pjoin(wf.out_dir, cfg.outlier_feature_folder_name)
      makedirs(feat_dir, exist_ok=True)
      path_to_csv = lambda sid_: pjoin(feat_dir, sid_, out_csv_name)
  
      if feature_type in ['histogram_whole_scan', ]:
          extract_method = t1_histogram_whole_scan
      else:
          raise NotImplementedError('Requested feature type {} not implemented!\n'
                                    '\tAllowed options : {} '
                                    ''.format(feature_type, cfg.t1_mri_features_OLD))
  
      feature_paths = dict()
      num_subjects = len(wf.id_list)
      for counter, sid in enumerate(wf.id_list):
          print('{} : {}/{}'.format(sid, counter + 1, num_subjects))
          makedirs(pjoin(feat_dir, sid), exist_ok=True)
          feat_file = path_to_csv(sid)
          if not pexists(feat_file):
              features = extract_method(wf.path_getter_inputs(sid))
              try:
                  np.savetxt(feat_file, features, delimiter='\n', header=feature_type)
              except:
                  raise IOError('Unable to save extracted features to disk!')
  
          feature_paths[sid] = feat_file
  
      return feature_paths
  
  
  def functional_mri_features(*args):
      """Extractor from functional MRI scan"""
  
      raise NotImplementedError
  
  def diffusion_mri_features(*args):
      """Extractor from diffusion MRI scan"""
  
      raise NotImplementedError
  