Просмотр исходного кода

gin commit from SUND33778

New files: 69
Modified files: 1
Deleted files: 1
Frederik Filip Stæger 4 лет назад
Родитель
Сommit
26251fd035
71 измененных файлов с 1326 добавлено и 0 удалено
  1. 47 0
      code_template_creation/README.md
  2. 288 0
      code_template_creation/co_registration.py
  3. 329 0
      code_template_creation/helper_functions.py
  4. 105 0
      code_template_creation/main.py
  5. 227 0
      code_template_creation/slice_to_template.py
  6. 266 0
      code_template_creation/template_creation.py
  7. 0 0
      data/mice/preprocessed/data.txt
  8. 1 0
      data/mice/tif/BM10/BM10_S01.tif
  9. 1 0
      data/mice/tif/BM10/BM10_S02.tif
  10. 1 0
      data/mice/tif/BM10/BM10_S03.tif
  11. 1 0
      data/mice/tif/BM10/BM10_S04.tif
  12. 1 0
      data/mice/tif/BM10/BM10_S05.tif
  13. 1 0
      data/mice/tif/BM10/BM10_S06.tif
  14. 1 0
      data/mice/tif/BM10/BM10_S07.tif
  15. 1 0
      data/mice/tif/BM10/BM10_S08.tif
  16. 1 0
      data/mice/tif/BM10/BM10_S09.tif
  17. 1 0
      data/mice/tif/BM10/BM10_S10.tif
  18. 1 0
      data/mice/tif/BM10/BM10_S11.tif
  19. 1 0
      data/mice/tif/BM10/BM10_S12.tif
  20. 1 0
      data/mice/tif/BM10/BM10_S13.tif
  21. 1 0
      data/mice/tif/BM10/BM10_S14.tif
  22. 1 0
      data/mice/tif/BM10/BM10_S15.tif
  23. 1 0
      data/mice/tif/BM10/BM10_S16.tif
  24. 1 0
      data/mice/tif/BM10/BM10_S17.tif
  25. 1 0
      data/mice/tif/BM10/BM10_S18.tif
  26. 1 0
      data/mice/tif/BM10/BM10_S19.tif
  27. 1 0
      data/mice/tif/BM10/BM10_S20.tif
  28. 1 0
      data/mice/tif/BM10/BM10_S21.tif
  29. 1 0
      data/mice/tif/BM10/BM10_S22.tif
  30. 1 0
      data/mice/tif/BM10/BM10_S23.tif
  31. 1 0
      data/mice/tif/BM10/BM10_S24.tif
  32. 1 0
      data/mice/tif/BM10/BM10_S25.tif
  33. 1 0
      data/mice/tif/BM10/BM10_S26.tif
  34. 1 0
      data/mice/tif/BM10/BM10_S27.tif
  35. 1 0
      data/mice/tif/BM10/BM10_S28.tif
  36. 1 0
      data/mice/tif/BM10/BM10_S29.tif
  37. 1 0
      data/mice/tif/BM10/BM10_S30.tif
  38. 1 0
      data/mice/tif/BM10/BM10_S31.tif
  39. 1 0
      data/mice/tif/BM10/BM10_S32.tif
  40. 1 0
      data/mice/tif/BM10/BM10_S33.tif
  41. 1 0
      data/mice/tif/BM10/BM10_S34.tif
  42. 1 0
      data/mice/tif/BM10/BM10_S35.tif
  43. 1 0
      data/mice/tif/BM10/BM10_S36.tif
  44. 1 0
      data/mice/tif/BM10/BM10_S37.tif
  45. 1 0
      data/mice/tif/BM10/BM10_S38.tif
  46. 1 0
      data/mice/tif/BM10/BM10_S39.tif
  47. 1 0
      data/mice/tif/BM10/BM10_S40.tif
  48. 1 0
      data/mice/tif/BM10/BM10_S41.tif
  49. 1 0
      data/mice/tif/BM10/BM10_S42.tif
  50. 1 0
      data/mice/tif/BM10/BM10_S43.tif
  51. 1 0
      data/mice/tif/BM10/BM10_S44.tif
  52. 1 0
      data/mice/tif/BM10/BM10_S45.tif
  53. 1 0
      data/mice/tif/BM10/BM10_S46.tif
  54. 1 0
      data/mice/tif/BM10/BM10_S47.tif
  55. 1 0
      data/mice/tif/BM10/BM10_S48.tif
  56. 1 0
      data/mice/tif/BM10/BM10_S49.tif
  57. 1 0
      data/mice/tif/BM10/BM10_S50.tif
  58. 1 0
      data/mice/tif/BM10/BM10_S51.tif
  59. 1 0
      data/mice/tif/BM10/BM10_S52.tif
  60. 1 0
      data/mice/tif/BM10/BM10_S53.tif
  61. 1 0
      data/mice/tif/BM10/BM10_S54.tif
  62. 1 0
      data/mice/tif/BM10/BM10_S55.tif
  63. 1 0
      data/mice/tif/BM10/BM10_S56.tif
  64. 1 0
      data/mice/tif/BM10/BM10_S57.tif
  65. 1 0
      data/mice/tif/BM10/BM10_S58.tif
  66. 1 0
      data/mice/tif/BM10/BM10_S59.tif
  67. 1 0
      data/mice/tif/BM10/BM10_S60.tif
  68. 1 0
      data/mice/tif/BM10/BM10_S61.tif
  69. 1 0
      data/mice/tif/BM10/BM10_S62.tif
  70. 1 0
      data/mice/tif/BM10/BM10_S63.tif
  71. 1 0
      data/mice/tif/BM10/BM10_S64.tif

+ 47 - 0
code_template_creation/README.md

@@ -0,0 +1,47 @@
+# DAPI template pipeline
+The original pipeline for pre-processing, reconstructing, and template
+creation of the DAPI template.
+
+## Description
+The reconstruction and template creation was run twice.
+First with the allen reference atlas as reference without non-linear
+registration steps resulting in an initial DAPI template. The initial DAPI
+template was used as reference (instead of the Allen reference atlas)
+in the second run of reconstruction and template creation, resulting in the
+final DAPI template. The two runs of the pipeline were manually modified.
+
+This pipeline is based on the original data structure of the slice images
+with a few sanity checks to make sure that all images are present. To run this
+pipeline with the data structure from the GIN-repository you need to remove
+these sanity checks, get the mouse ids from a different directory (like tif/),
+and get all the image paths in each individual tif folder.
+
+## Overview
+The main script calls the pre_processing, co_registration, and
+template_creation with appropriate parameters and paths. In pre_processing
+the original tif image is down-sampled to 8.6x8.6 um and masked according to
+the biggest (except background) connected component after smoothing and
+threshold to remove artefact outside of the brain slice.
+After pre-processing the slices for each brain is reconstructed
+into 3-dimensional brain volumes. This reconstruction is carried out by 
+iterative registrations (both between volumes and between slice-pairs) to
+a reference (Allen or DAPI from first iteration). Finally, each reconstructed
+brain volume is used in the creation of a population-based average in 
+template_creation.
+
+## Packages
+This pipeline uses the following non-standard packages (package name: version):
+
+```
+nibabel: 2.1.0
+nipype: 0.13.1
+numpy: 1.15.1
+PIL: 5.1.0
+scipy: 1.1.0
+skimage: 0.14.0
+sklearn: 0.19.2
+```
+
+## Registration software
+A precompiled binary of Advanced Normalization Tools (ANTs) V.2.1.0 for 
+linux/mac was used.

+ 288 - 0
code_template_creation/co_registration.py

@@ -0,0 +1,288 @@
+from helper_functions import pretty, make_dirs, slice_volume, files_in_dir, files_ind_dir_wo_string, make_volume
+from nipype.interfaces import ants
+from os import path
+from shutil import rmtree
+import sys
+import getopt
+
+# Help message.
+tab = '     '
+usage_message = '\nCOMMAND:\n' + tab + sys.argv[0] + \
+                '\n\nOPTIONS:\n' + \
+                tab + '-h, help menu\n' + \
+                tab + '-e, --template, <input template>\n' + \
+                tab + '-s, --slices, <directory with input slices>\n' + \
+                tab + '-r, --rawvolume, <raw volume of slices>\n' + \
+                tab + '-t, --inittrans, <initial transform, roughly transforming raw slice volume to match input ' \
+                      'volume, manually created>\n' + \
+                tab + '-d, --id, <brain id>\n' + \
+                tab + '-o, --output, <output directory, will create new directory if not present>\n' + \
+                tab + '-l, --log, [bool]>\n'
+
+
+def registration(fixed, moving, result, reg_type, sigma, shrink, param, dim, initial_trans='', inverse_result=''):
+    """
+    Registration function. Registeres the moving image to the fixed with given parameters. If initial transform is not
+    given then the images are aligned by center of mass. Outputs inversely transformed result if given.
+    :param fixed: Path to fixed image.
+    :param moving: Path to moving image.
+    :param result: Path to result image.
+    :param reg_type: Registration type. List containing, Rigid, Affine, and/or SyN.
+    :param sigma: List of sigma parameters. Ordered according to the reg_type parameter.
+    :param shrink: List of shrink parameters. Ordered according to the reg_type parameter.
+    :param param: List of transform parameters. Ordered according to the reg_type parameter.
+    :param dim: Dimension of images. Typically 2 or 3.
+    :param initial_trans: Optional path to initial moving transform. If not given, the images will be matched initially
+    by aligning the center of mass.
+    :param inverse_result: Optional path to the inversely transformed fixed image.
+    :return: Returns nothing.
+    """
+    # Extract number of registrations.
+    steps = len(reg_type)
+
+    # Add 1000 iterations for each step.
+    if steps == 1:
+        iterations = [[1000] * len(sigma[0])]
+    else:
+        iterations = []
+        for i, reg in enumerate(reg_type):
+            iteration = [1000] * len(sigma[i])
+            iterations.append(iteration)
+
+    # Create registration instance.
+    reg_instance = ants.Registration(dimension=dim,
+                                     transforms=reg_type,
+                                     transform_parameters=param,
+                                     metric=['MeanSquares']*steps,
+                                     interpolation='BSpline',
+                                     fixed_image=[fixed],
+                                     moving_image=[moving],
+                                     metric_weight=[1]*steps,
+                                     radius_or_number_of_bins=[32]*steps,
+                                     number_of_iterations=iterations,
+                                     smoothing_sigmas=sigma,
+                                     shrink_factors=shrink,
+                                     convergence_threshold=[1.e-7],
+                                     sampling_strategy=['Regular']*steps,
+                                     use_histogram_matching=True,
+                                     winsorize_lower_quantile=0.05,
+                                     winsorize_upper_quantile=0.95,
+                                     write_composite_transform=True,
+                                     output_warped_image=result,
+                                     output_transform_prefix=result.split('.')[0] + '-Transformation',
+                                     num_threads=12)
+
+    # Add initial moving transform if given, else match by center of mass.
+    if not initial_trans == '':
+        reg_instance.inputs.initial_moving_transform = initial_trans
+    else:
+        reg_instance.inputs.initial_moving_transform_com = 1
+
+    # Output reverse results if path is given.
+    if not inverse_result == '':
+        reg_instance.inputs.output_inverse_warped_image = inverse_result
+
+    # Run registration.
+    reg_instance.run()
+
+
+def co_register(template, slices, raw_volume, initial_transform, out_dir, brain_id, r=3, a=2, nl=3, print_log=False):
+    """
+    Function for reconstructing brain volume by iteratively registering to given template. The slices are stacked into a
+    3-dimensional volume that is registered to the template. The template is inversely transformed and sliced, resulting
+    in slice pairs with a template slice and an input slice. For each pair, the input slice is registered to the
+    template slice, and all the resulting registered slices are stacked to a volume. This process is repeated r times
+    rigid, a times rigid and affine, and nl times rigid, affine, and non-linear. Returns the path to the final
+    reconstructed brain volume.
+    :param template: Path to template used as a reference for brain reconstruction.
+    :param slices: List of paths to slices that needs to be reconstructed.
+    :param raw_volume: Path to the initial stacked volume, needed for first rigid registration step.
+    :param initial_transform:Initial 3-dimensional transform of the raw volume to the template. If empty string given,
+    they will be matched by the center of mass.
+    :param out_dir: Output directory.
+    :param brain_id: Mouse id.
+    :param r: Int [Default: 3]. Number of rigid registrations.
+    :param a: Int [Default: 2]. Number of rigid and affine registrations.
+    :param nl: Int [Default: 3]. Number of rigid, affine, and non-linear registrations.
+    :param print_log: Boolean [Default: False]. Will print log if true.
+    :return: Return path to the final reconstructed brain volume.
+    """
+
+    # Determine whether the reference template is the Allen atlas (1. iteration) or the DAPI-template (2. iteration).
+    if 'allen' in template:
+        template_type = 'allen'
+    else:
+        template_type = 'dapi'
+    volume_postfix = '_hv_reg_' + template_type + '_' + brain_id
+
+    # List of slices.
+    raw_slices = files_in_dir(slices, '.nii')
+
+    if print_log:
+        print('Volume given interpreted as:', template_type)
+        print('Checking paths.')
+
+    if not path.exists(raw_volume):
+        print('The path:', raw_volume, 'does not exist.')
+    if not path.exists(template):
+        print('The path:', template, 'does not exist.')
+    if not path.exists(initial_transform):
+        print('The path:', initial_transform, 'does not exist.')
+
+    if print_log:
+        print('Defining iterations.')
+    reg_types = ['Rigid'] * r + ['Affine'] * a + ['SyN'] * nl
+
+    if print_log:
+        print(reg_types)
+
+    # Run through iterations.
+    for i, iteration in enumerate(reg_types):
+        cur_work_dir = out_dir + pretty(i) + '/'
+        make_dirs(cur_work_dir)
+
+        if print_log:
+            print('Running iteration', pretty(i))
+            print('Current work dir:', cur_work_dir)
+
+        if i == 0:
+            hv = raw_volume
+        else:
+            hv = out_dir + pretty(i) + volume_postfix + '.nii'
+            if print_log:
+                print('Deleting prev. iteration work dir:', out_dir + pretty(i - 1) + '/')
+            rmtree(out_dir + pretty(i - 1) + '/')
+
+        # Iteration specific registration parameters.
+        if iteration == 'Rigid':
+            reg = ['Rigid']
+            params = [(0.25,)]
+            vol_sigma = [[4, 4, 2, 2]]
+            vol_shrink = [[32, 16, 8, 4]]
+            slice_sigma = [[4, 4, 2, 2]]
+            slice_shrink = [[32, 16, 8, 4]]
+            if i == 0:
+                vol_sigma = [[4, 4, 4]]
+                vol_shrink = [[32, 16, 8]]
+                slice_sigma = [[4, 4, 2]]
+                slice_shrink = [[32, 16, 8]]
+        elif iteration == 'Affine':
+            reg = ['Rigid', 'Affine']
+            params = [(0.25,), (0.25,), ]
+            vol_sigma = [[4, 4, 2, 2], [4, 4, 2, 2]]
+            vol_shrink = [[32, 16, 8, 4], [32, 16, 8, 4]]
+            slice_sigma = [[4, 4, 2, 2], [4, 4, 2, 2]]
+            slice_shrink = [[32, 16, 8, 4], [32, 16, 8, 4]]
+        elif iteration == 'SyN':
+            reg = ['Rigid', 'Affine', 'SyN']
+            params = [(0.25,), (0.25,), (0.15, 3, 0.5)]
+            vol_sigma = [[4, 4, 2, 2], [4, 4, 2, 2], [4, 4, 4, 2]]
+            vol_shrink = [[32, 16, 8, 4], [32, 16, 8, 4], [64, 32, 16, 8]]
+            slice_sigma = [[4, 4, 2, 2], [4, 4, 2, 2], [4, 4, 4, 2, 2]]
+            slice_shrink = [[32, 16, 8, 4], [32, 16, 8, 4], [64, 32, 16, 8, 4]]
+            if i == len(reg_types)-1:
+                slice_sigma = [[4, 4, 2, 2], [4, 4, 2, 2], [4, 4, 4, 2, 2, 1]]
+                slice_shrink = [[32, 16, 8, 4], [32, 16, 8, 4], [64, 32, 16, 8, 4, 2]]
+        else:
+            sys.exit('The registration type "' + str(iteration) + '" does not exist. Reconstruction stopped.')
+
+        # Define name and path for registered template to hv.
+        cur_reg_name = pretty(i) + '-' + template_type + '_reg_to_hv.nii'
+        cur_reg_path = cur_work_dir + cur_reg_name
+
+        if print_log:
+            print('Registering hv to given volume - either MR or atlas')
+
+        if not path.exists(cur_reg_path):
+            registration(template, hv, cur_work_dir + 'hv_to_template.nii', reg, vol_sigma,
+                         vol_shrink, params, dim=3, initial_trans=initial_transform,
+                         inverse_result=cur_reg_path)
+
+        # Define and create directory for sliced template.
+        template_slice_dir = cur_work_dir + cur_reg_name.split('.')[0] + '-slices/'
+        make_dirs(template_slice_dir)
+
+        if print_log:
+            print('Slicing', cur_reg_path, 'into', template_slice_dir)
+
+        # Slice template.
+        slice_volume(cur_reg_path, template_slice_dir)
+        template_slices = files_in_dir(template_slice_dir, '.nii')
+
+        if len(raw_slices) == len(template_slices):
+            print('Number of images matches.')
+        else:
+            sys.exit('Mismatch between number of images.')
+
+        # Make directory for slice to slice registrations.
+        reg_slices_dir = cur_work_dir + pretty(i) + '-raw_reg_template/'
+        make_dirs(reg_slices_dir)
+
+        # Registered slice postfix.
+        reg_slice_postfix = '_reg'
+
+        # Perform slice to slice registrations.
+        for n, (raw_slice, template_slice) in enumerate(zip(raw_slices, template_slices)):
+            reg_slice = reg_slices_dir + pretty(i) + '_S' + pretty(n) + reg_slice_postfix + '.nii'
+            if not path.exists(reg_slice):
+                registration(template_slice, raw_slice, reg_slice, reg,
+                             slice_sigma, slice_shrink, params, 2)
+
+        # Stack registered slices into new volume.
+        reg_slices = files_ind_dir_wo_string(reg_slices_dir, exclusion_string='-Transformation')
+        final_volume = out_dir + pretty(i + 1) + volume_postfix + '.nii'
+        make_volume(reg_slices, final_volume)
+
+    # Return final reconstructed brain volume.
+    return final_volume
+
+
+if __name__ == '__main__':
+    arguments = sys.argv
+    log = False
+
+    if len(arguments) == 1:
+        print(usage_message)
+    else:
+        try:
+            opts, args = getopt.getopt(arguments[1:],
+                                       "he:s:r:t:d:o:l:",
+                                       ["template=", "slices=", "rawvolume", "inittrans", "id", "output", "log="])
+        except getopt.GetoptError:
+            print('\nSomething is not right in your syntax. Tak a look at your possibilities:\n', usage_message)
+            sys.exit(2)
+        for opt, arg in opts:
+            if opt == "-h":
+                print(usage_message)
+                sys.exit()
+            elif opt in ("-e", "--template"):
+                input_template = arg
+            elif opt in ("-s", "--slices"):
+                input_slices = arg
+            elif opt in ("-r", "--rawvolume"):
+                input_raw_volume = arg
+            elif opt in ("-t", "--inittrans"):
+                input_init_trans = arg
+            elif opt in ("-d", "--id"):
+                input_brain_id = arg
+            elif opt in ("-o", "--output"):
+                output_dir = arg
+            elif opt in ("-l", "--log"):
+                if arg == 'True' or arg == 'true':
+                    input_print_log = True
+                elif arg == 'False' or arg == 'false':
+                    input_print_log = False
+                else:
+                    print('\nSomething is not right in your syntax. Tak a look at your possibilities:\n', usage_message)
+                    sys.exit(2)
+        try:
+            co_register(input_template,
+                        input_slices,
+                        input_raw_volume,
+                        input_init_trans,
+                        output_dir,
+                        input_brain_id,
+                        print_log=input_print_log)
+        except NameError:
+            print('\nYou are not (properly) defining all the input variables. Tak a look at what is needed:\n',
+                  usage_message)

+ 329 - 0
code_template_creation/helper_functions.py

@@ -0,0 +1,329 @@
+import os
+import nibabel as nib
+import numpy as np
+from nipype.interfaces.ants.segmentation import N4BiasFieldCorrection
+import math
+from sklearn.metrics import mutual_info_score
+from copy import copy
+
+
+# Makes directory if not already present.
+def make_dirs(paths):
+    if isinstance(paths, str):
+        if not os.path.exists(paths):
+            os.makedirs(paths)
+            print(paths + ' directory made.')
+    elif isinstance(paths, list):
+        for path in paths:
+            if not os.path.exists(path):
+                os.makedirs(path)
+                print(path + ' directory made.')
+    else:
+        print('The given path was neither a single path or a list of paths')
+
+
+# Get list of paths of files in dir with specific file type.
+def files_in_dir(path, file_type=''):
+    path_list = []
+    for file in os.listdir(path):
+        if file.startswith('.'):
+            continue
+        if file.endswith(file_type):
+            path_list.append(path + file)
+    return sorted(path_list)
+
+
+def files_ind_dir_wo_string(path, exclusion_string):
+    path_list = []
+    for file in os.listdir(path):
+        if exclusion_string not in file:
+            path_list.append(path + file)
+    return sorted(path_list)
+
+
+# Get list of images with specific file type in folders. Also returns list of folders.
+def images_in_folders(path, file_tyoe=''):
+    path_list = []
+    folder_list = []
+    for folder in os.listdir(path):
+        folder_list.append(path + folder)
+        for file in os.listdir(path + folder + '/'):
+            if file.startswith('.'):
+                continue
+            if file.endswith(file_tyoe):
+                path_list.append(path + folder + '/' + file)
+    return sorted(path_list), sorted(folder_list)  # sorted added by FFS 26/6/2019.
+
+
+# Make .nii image from raw data. Will give specific pixel dimensions, if given.
+def make_nii(data, pixdim=[]):
+    nii = nib.Nifti1Image(data, np.eye(4))
+    if len(pixdim) != 0:
+        hdr = nii.header
+        hdr['pixdim'] = pixdim
+        nii = nib.Nifti1Pair(data, None, hdr)
+    return nii
+
+
+# Make volume from list of consecutive images.
+def make_volume(images, output):
+    init_image = nib.load(images[0])
+    width, height = init_image.shape
+    depth = len(images)
+    init_pixdim = init_image.header['pixdim']
+    volume_array = np.zeros((width, depth, height))
+
+    for i, image in enumerate(images):
+        image_data = np.rot90(nib.load(image).get_data(), 2)
+        volume_array[:, depth - i - 1, :] = image_data
+    nib.save(make_nii(volume_array, [1., init_pixdim[1], 0.100, init_pixdim[2], 1., 1., 1., 1.]), output)
+
+
+# Get parent directory.
+def get_parent_dir(path):
+    folders = path.split('/')
+    if folders[-1] == '':
+        parent_dir = '/'.join(folders[0:-2]) + '/'
+    else:
+        parent_dir = '/'.join(folders[0:-1]) + '/'
+    return parent_dir
+
+
+# Reorient MR
+def reorient_mr(MR, result, inverted=False):
+    original = nib.load(MR)
+    original_data = original.get_data()
+    pixdim = original.header['pixdim']
+    if inverted:
+        reoriented_data = np.fliplr(np.rot90(original_data, 1, axes=(1, 2)))
+    else:
+        reoriented_data = np.rot90(original_data, 1, axes=(1, 2))
+    nib.save(make_nii(reoriented_data, [1., pixdim[1], pixdim[3], pixdim[2], 1., 1., 1., 1.]), result)
+
+
+# Reorient atlas
+def reorient_atlas(atlas, result):
+    original = nib.load(atlas)
+    original_data = original.get_data()
+    pixdim = original.header['pixdim']
+    print(original_data.shape)
+    reoriented_data = np.reshape(original_data, (456, 528, 320), order='A')
+    print(reoriented_data.shape)
+    nib.save(make_nii(reoriented_data, [1., pixdim[1], pixdim[3], pixdim[2], 1., 1., 1., 1.]), result)
+
+
+def slice_volume(volume, slices_path):
+    current_mr = nib.load(volume)
+    current_data = current_mr.get_data()
+    number_of_slices = current_data.shape[1]
+    pixdim = current_mr.header['pixdim']
+
+    for i in range(number_of_slices):
+        image_nr = i + 1
+        image_nr_beauty = '0' + str(image_nr) if image_nr < 10 else str(image_nr)
+        nib.save(make_nii(np.rot90(current_data[:, number_of_slices - image_nr, :], 2),
+                          [1., pixdim[1], pixdim[3], pixdim[2], 1., 1., 1., 1.]),
+                 slices_path + 'Template_S' + image_nr_beauty + '.nii')
+
+
+def bias_field_correction(image, output, dim=2):
+    N4BiasFieldCorrection(
+        input_image=image,
+        output_image=output,
+        dimension=dim
+    ).run()
+
+
+def flip_volume_lr(image, output):
+    nii = nib.load(image)
+    hdr = nii.header
+    data = nii.get_data()
+    flipped = np.flip(data, axis=0)
+    nib.save(nib.Nifti1Pair(flipped, None, hdr), output)
+
+
+def add_slash_to_path(path):
+    return path + '/' if not path[-1] == '/' else path
+
+
+def identity_transform(work_dir, dimensions):
+    path = work_dir + 'identity_transform.txt'
+    identity_transform = open(path, 'w+')
+    if dimensions == 3:
+        identity_transform.write('Transform: MatrixOffsetTransformBase_double_3_3'
+                                 '\nParameters: 1 0 0 0 1 0 0 0 1 0 0 0'
+                                 '\nFixedParameters: 0 0 0\n')
+    elif dimensions == 2:
+        identity_transform.write('Transform: MatrixOffsetTransformBase_double_2_2'
+                                 '\nParameters: 1 0 0 1 0 0'
+                                 '\nFixedParameters: 0 0\n')
+    else:
+        print('Helper function identity_transform does only support 2d and 3d.')
+    return path
+
+
+# Convert int to string with two places (1, 7, 13) --> (01, 07, 13).
+def pretty(i):
+    return '0' + str(i) if i < 10 else str(i)
+
+
+# Returns True i given path exists.
+def path_exists(path):
+    return True if os.path.exists(path) else False
+
+
+def robust_avg_volumes(volumes, output):
+    # Determine images matrix dimensions.
+    depth_list = []
+    init_nii = nib.load(volumes[0])
+    pix_dim = init_nii.header['pixdim']
+    a, b, c = init_nii.shape
+    d = len(volumes)
+
+    for volume_shape in volumes:
+        depth_list.append(nib.load(volume_shape).shape[1])
+
+    b = max(depth_list)
+
+    matrix_shape = (a, b, c, d)
+    images_matrix = np.zeros(matrix_shape)
+    print('Average:')
+    for i, volume in enumerate(volumes):
+        print('Adding', volume)
+        nii = nib.load(volume)
+        images_matrix[:, 0:nii.shape[1], :, i] = nii.get_data()
+
+    # Calculate mean and std.
+    images_mean = np.mean(images_matrix, axis=3)
+    images_std = images_matrix.std(3)
+    images_std_scaled = images_std * 2
+
+    # Duplicate mean and std to full dimensions for easy matching.
+    mean_matrix = np.repeat(images_mean[:, :, :, np.newaxis], d, axis=3)
+    scaled_std_matrix = np.repeat(images_std_scaled[:, :, :, np.newaxis], d, axis=3)
+
+    robust_matrix = np.where(
+        (images_matrix < mean_matrix - scaled_std_matrix) | (images_matrix > mean_matrix + scaled_std_matrix),
+        np.nan,
+        images_matrix)
+
+    robust_mean = np.nanmedian(robust_matrix, axis=3)
+    nib.save(make_nii(robust_mean, pixdim=pix_dim), output)
+
+
+def split_images(images, output_folder, max_depth=50):
+    temp_folder = output_folder
+    make_dirs(temp_folder)
+
+    for image in images:
+        # Extract name from path.
+        name = image.split('/')[-1].split('.')[0]
+
+        # Load nii, header and data.
+        nii = nib.load(image)
+        header = nii.header
+        data = nii.get_data()
+
+        # Get depth from header and calculate number of parts.
+        depth = nii.shape[1]
+        nr_of_parts = math.ceil(depth / max_depth)
+
+        # If first iteration create empty list of list of parts.
+        if image == images[0]:
+            parts = [[] for _ in range(nr_of_parts)]
+            output_shape = nii.shape
+            output_header = copy(header)
+
+        # Make parts.
+        for i in range(nr_of_parts):
+            # Define form and to index.
+            from_i = i * max_depth
+            to_i = min((i + 1) * max_depth - 1, depth)
+
+            # Calculate depth of current part. Set in nii header.
+            cur_depth = to_i - from_i + 1
+            header['dim'][2] = cur_depth
+
+            # Slice data.
+            cur_data = data[:, from_i:to_i+1, :]
+            cur_path = temp_folder + name + '_' + str(i) + '.nii'
+
+            # Append to parts list.
+            parts[i] += [cur_path]
+
+            # Save part.
+            nib.save(nib.Nifti1Image(cur_data, None, header), cur_path)
+
+    return parts, output_shape, output_header
+
+
+def robust_large_images(images, output, max_depth=50):
+    temp_folder = get_parent_dir(images[0]) + 'avg_temp_volumes/'
+    parts, output_shape, output_header = split_images(images, temp_folder, max_depth=max_depth)
+
+    avgs = []
+    for n, part in enumerate(parts):
+        print('Averaging: ' + str(part))
+        cur_avg = temp_folder + str(n) + '_avg.nii'
+        robust_avg_volumes(part, cur_avg)
+        avgs += [cur_avg]
+
+    final_avg = np.zeros(output_shape)
+
+    for m, avg in enumerate(avgs):
+        print('Appending avg: ' + str(avg))
+        final_avg[:, m*max_depth:min((m+1)*max_depth, output_shape[1]), :] = nib.load(avg).get_data()
+
+    nib.save(nib.Nifti1Image(final_avg, None, output_header), output)
+
+
+def avg_template(image, template, template_weight, output):
+    print(image, template)
+    a = nib.load(image)
+    b = nib.load(template)
+    pix_dim = a.header['pixdim']
+
+    depth = max([a.shape[1], b.shape[1]])
+
+    a_scaled = np.zeros((a.shape[0], depth, a.shape[2]))
+    b_scaled = np.zeros((a.shape[0], depth, a.shape[2]))
+
+    a_scaled[:a.shape[0], :a.shape[1], :a.shape[2]] = a.get_data() * (1 - template_weight)
+    b_scaled[:b.shape[0], :b.shape[1], :b.shape[2]] = b.get_data() * template_weight
+
+    weighted = (a_scaled + b_scaled) / 2
+    nib.save(make_nii(weighted, pixdim=pix_dim), output)
+
+
+def normal_list_1d(sigma, n, mu=0.5, max_is_one=False):
+    """
+    Normal distribution.
+    :param sigma: sigma.
+    :param n: number of values.
+    :param mu: [0.0:1.0] a float determining the relative position of the peak. 0.0 left, 1.0 right. Default: 0.5.
+    :return: [x] a list of the natural numbers from 0 to n. [y]: normal value
+    """
+    mu = mu*n - 0.5
+    x = [x for x in range(n)]
+    y = [1 / (math.sqrt(2 * math.pi * sigma ** 2)) * math.e ** ((-(xi - mu) ** 2) / (2 * sigma ** 2)) for xi in x]
+
+    if max_is_one:
+        max_y = max(y)
+        y_adjusted = [yi * 1/max_y for yi in y]
+        return x, y_adjusted
+
+    return x, y
+
+
+def mutual_information(x, y, bins):
+    c_xy = np.histogram2d(x.ravel(), y.ravel(), bins)[0]
+    mi = mutual_info_score(None, None, contingency=c_xy)
+    return mi
+
+
+def immediate_sub_dirs(directory, full_path=True):
+    sub_dirs = sorted(next(os.walk(directory))[1])
+    if full_path:
+        return [directory + x + '/' for x in sub_dirs]
+    else:
+        return sub_dirs

+ 105 - 0
code_template_creation/main.py

@@ -0,0 +1,105 @@
+from pre_processing import pre_process
+from co_registration import co_register
+from template_creation import create_template_from_images
+import helper_functions as hf
+import sys
+import os
+from shutil import copyfile
+
+
+def pre_process_brain_tiffs(tif_path, result_path, mouse_id):
+    """
+    Utility function pre-processing all tiffs in folder, outputting pre-processed nifties in result_path.
+    :param tif_path: The tiffs directory.
+    :param result_path: The output directory.
+    :param mouse_id: The mouse id.
+    :return: Returns nothing.
+    """
+    # Get all images and the enclosing tiff folders.
+    images, folders = hf.images_in_folders(tif_path)
+
+    # Ensure that the number of images matches the naming of the folders
+    if len(images) == int(folders[-1][-2:]):
+        print('Number of images matches the naming on the folders')
+    else:
+        sys.exit('Mismatch between number of images and naming of last TIF folder.')
+
+    # Make output directory.
+    hf.make_dirs([result_path])
+
+    # Loop through each image and pre-process.
+    for image_nr, image_path in enumerate(images, 1):
+        image_nr_beauty = hf.pretty(image_nr)
+        output = result_path + mouse_id + '_S' + image_nr_beauty + '.nii'
+        if not os.path.exists(output):
+            pre_process(image_path, output)
+
+
+def main(preprocess=False, coreg=False, make_template=False):
+    """
+    Main function performing pre-processing of tiffs, 3-dimensional reconstruction to reference and population-based
+    template creation.
+    :param preprocess: Boolean [Default: False]. Will perform pre-processing of tiffs if set to true.
+    :param coreg: Boolean [Default: False]. Will perform brain reconstruction based on the sequential slices if set to
+    true.
+    :param make_template: Boolean [Default: False]. Will perform population-based template creation based on the
+    reconstructed brain volumes if set to true.
+    :return: Returns nothing.
+    """
+    # Working directory
+    sandbox = '/run/media/frederik/S_T1/frederik_filip_mqr375/auto-seg/' # Should be removed for final submission.
+
+    # Get list of mice ids.
+    sandbox_mice = sandbox + 'mice/'
+    mice = os.listdir(sandbox_mice)
+
+    if coreg or preprocess:
+        # Loop through mice ids.
+        for mouse in sorted(mice):
+            # Current mouse directory and subdirectories.
+            work_dir = sandbox_mice + mouse + '/'
+            raw_dir = work_dir + 'TIF/'
+            pre_processed_dir = work_dir + 'preprocessed/'
+            out_dir = work_dir + 'volumes/'
+
+            # Make volumes directory.
+            hf.make_dirs(out_dir)
+
+            # Define raw volume and initial transform path (if present).
+            initial_vol_trans = out_dir + 'initial_transform_' + mouse + '.txt'
+            raw_vol_path = out_dir + '00_hv_raw_' + mouse + '.nii'
+
+            # Pre process tiff images.
+            if preprocess:
+                pre_process_brain_tiffs(raw_dir, pre_processed_dir, mouse)
+
+            # Create raw volume if not already present.
+            if not os.path.exists(raw_vol_path):
+                hf.make_volume(hf.files_in_dir(pre_processed_dir, '.nii'), raw_vol_path)
+
+            # Check that an initial transform is present.
+            if not os.path.exists(initial_vol_trans):
+                sys.exit('An initial moving transform was not found for at least brain with id : ' + mouse +
+                         '\nMake sure it is named and placed exactly: ' + initial_vol_trans)
+
+            # -- This call should be manually modified depending on first or second iteration -- #
+            # Perform co-registration to reference (Allen in first iteration. DAPI template in second iteration).
+            # Manually created initial transform is only used in the first iteration.
+            # First iteration had 0 non-linear steps (parameter nl=0).
+            if coreg:
+                mouse_coreg = co_register(sandbox + 'second-iteration_average_allen_0_15.nii.gz',
+                                          pre_processed_dir, raw_vol_path, '', out_dir, mouse,
+                                          print_log=True, nl=2)
+                copyfile(mouse_coreg, sandbox + 'volumes/' + mouse + '.nii')
+
+    # Population based template creation using symmetric modelling.
+    if make_template:
+        create_template_from_images(
+            sandbox + 'volumes/',
+            sandbox + 'template/',
+            symmetric=True
+        )
+
+
+if __name__ == '__main__':
+    main(preprocess=True, coreg=True, make_template=True)

+ 227 - 0
code_template_creation/slice_to_template.py

@@ -0,0 +1,227 @@
+import argparse
+from nipype.interfaces import ants
+import nibabel as nib
+import numpy as np
+import os
+from skimage.filters import threshold_otsu
+import matplotlib.pyplot as plt
+from helper_functions import make_nii, make_dirs, normal_list_1d, mutual_information
+from pre_processing import pre_process
+from skimage import morphology
+from scipy.ndimage import gaussian_filter
+import sys
+
+
+def slice_template(template, template_seg, out_dir, pos, padding=5):
+    # Load template data and header.
+    template = nib.load(template)
+    template_data = template.get_data()
+    template_header = template.header
+
+    # Load template segmentation data.
+    template_seg = nib.load(template_seg)
+    template_seg_data = template_seg.get_data()
+
+    # Set pixel dimensions.
+    slice_pixdim = [1., template_header['pixdim'][1], template_header['pixdim'][3], 1., 1., 1., 1., 1.]
+
+    images = []
+
+    for i in range(pos-padding, pos+padding+1):
+        output = out_dir + 'S' + str(i).zfill(2) + '.nii'
+        seg_output = out_dir + 'S' + str(i).zfill(2) + '_seg.nii'
+        if not os.path.exists(output):
+            nib.save(make_nii(np.rot90(template_data[:, i, :], 2), pixdim=slice_pixdim), output)
+        if not os.path.exists(seg_output):
+            nib.save(make_nii(np.rot90(template_seg_data[:, i, :], 2), pixdim=slice_pixdim), seg_output)
+        images.append(output)
+
+    # Returns a list of image paths.
+    return images
+
+
+def reg_slice(fixed, moving, output):
+    registration = ants.Registration(
+        dimension=2,
+        transforms=['Affine'],
+        transform_parameters=[(0.15, ), ],
+        initial_moving_transform_com=1,
+        metric=['MI'],
+        interpolation='BSpline',
+        fixed_image=[fixed],
+        moving_image=[moving],
+        metric_weight=[1],
+        radius_or_number_of_bins=[32],
+        number_of_iterations=[[1000, 1000, 1000, 1000, 1000]],
+        smoothing_sigmas=[[4, 4, 4, 2, 2]],
+        shrink_factors=[[64, 32, 16, 8, 4]],
+        convergence_threshold=[1.e-7],
+        sampling_strategy=['Regular'],
+        use_histogram_matching=True,
+        winsorize_lower_quantile=0.05,
+        winsorize_upper_quantile=0.95,
+        output_warped_image=output,
+        output_transform_prefix=output.split('.')[0] + '-Transformation',
+        num_threads=12
+    )
+    registration.run()
+
+
+def full_reg_slice(fixed, moving, output, seg):
+    output_pre = output.split('.')[0]
+    registration = ants.Registration(
+        dimension=2,
+        transforms=['Rigid', 'Affine', 'SyN'],
+        transform_parameters=[(0.15,), (0.15, ), (0.3, 0.3, 0.5)],
+        metric=['MeanSquares']*3,
+        initial_moving_transform_com=1,
+        interpolation='BSpline',
+        fixed_image=[fixed],
+        moving_image=[moving],
+        metric_weight=[1]*3,
+        radius_or_number_of_bins=[32]*3,
+        number_of_iterations=[[1000, 1000, 1000, 1000], [1000, 1000, 1000, 1000, 1000], [1000, 1000, 1000, 1000, 1000, 1000]],
+        smoothing_sigmas=[[4, 4, 2, 2], [4, 4, 4, 2, 2], [4, 4, 4, 2, 2, 2]],
+        shrink_factors=[[32, 16, 8, 4], [64, 32, 16, 8, 4], [64, 32, 16, 8, 4, 2]],
+        convergence_threshold=[1.e-7],
+        sampling_strategy=['Regular']*3,
+        use_histogram_matching=True,
+        winsorize_lower_quantile=0.05,
+        winsorize_upper_quantile=0.95,
+        output_warped_image=output,
+        output_transform_prefix=output_pre + '-Transformation-',
+        num_threads=12
+    )
+    registration.run()
+
+    # Define transformation to be applied on segmentation. Dependent on the system running ANTs.
+    if os.path.exists(output_pre + '-Transformation-0GenericAffine.mat'):
+        transform_paths = [output_pre + '-Transformation-1Warp.nii.gz',
+                           output_pre + '-Transformation-0GenericAffine.mat']
+    elif os.path.exists(output_pre + '-Transformation-0DerivedInitialMovingTranslation.mat'):
+        transform_paths = [output_pre + '-Transformation-3Warp.nii.gz',
+                           output_pre + '-Transformation-2Affine.mat',
+                           output_pre + '-Transformation-1Rigid.mat',
+                           output_pre + '-Transformation-0DerivedInitialMovingTranslation.mat']
+    else:
+        sys.exit('The ANTs-output transformation names/postfix was not recognised.')
+
+    # Apply recognised transformations to slice segmentation.
+    ants.ApplyTransforms(
+        dimension=2,
+        input_image=seg,
+        reference_image=output,
+        transforms=transform_paths,
+        interpolation='NearestNeighbor',
+        output_image=output.split('.')[0] + '_seg.nii',
+        num_threads=12
+    ).run()
+
+
+def main(input_slice_path, template_path, template_seg_path, output_path,
+         bregma, preprocess_input, inverse_output, padding=6, return_result_dict=False):
+    # Directory variables.
+    work_dir = os.path.dirname(output_path) + '/'
+    temp_dir = work_dir + 'temp/'
+    template_slice_dir = temp_dir + 'template_slices/'
+    make_dirs([temp_dir, template_slice_dir])
+
+    # Inverse bool
+    output_inverse = inverse_output != None
+    print(output_inverse)
+
+    # Slice template
+    coor = int(9.7431 * bregma + 38.7044)
+    template_slices = slice_template(template_path, template_seg_path, template_slice_dir, coor, padding=padding)
+
+    # Pre-process input image.
+    if preprocess_input in ['true', 'True']:
+        pre_processed_output = input_slice_path.split('.')[0] + '.nii'
+        pre_process(input_slice_path, pre_processed_output, print_log=True, shrink=1)
+        slice_path = pre_processed_output
+    else:
+        slice_path = input_slice_path
+
+    # Rigid registration
+    registration_dir = temp_dir + 'rigid_registrations/'
+    registered_slices = []
+    make_dirs(registration_dir)
+    for template_slice in template_slices:
+        name = template_slice.split('/')[-1].split('.')[0]
+        registered_slice = registration_dir + name + '.nii'
+        reg_slice(slice_path, template_slice, registered_slice)
+        registered_slices.append(registered_slice)
+
+    # Similarity extrema
+    mutual_informations = []
+    input_nii = nib.load(slice_path)
+    input_data = input_nii.get_data()
+    input_data_smooth = gaussian_filter(input_data, 2)
+
+    for image in registered_slices:
+        nii = nib.load(image)
+        data = nii.get_data()
+        mutual_informations.append(mutual_information(input_data_smooth, data, 32)-0.5)
+
+    # Weighted similarity.
+    _, weights = normal_list_1d(10, len(mutual_informations), max_is_one=True)
+    weighted_mi = [mi*w for mi, w in zip(mutual_informations, weights)]
+    max_y = max(weighted_mi)
+    max_index = weighted_mi.index(max_y)
+    maximum_slice = template_slices[max_index]
+    maximum_slice_seg = maximum_slice.split('.')[0] + '_seg.nii'
+
+    # Save pdf of similarities, with minimum marked.
+    f = plt.figure()
+    range_min = coor - padding
+    range_max = coor + padding + 1
+    max_x = range_min + max_index
+    plt.plot(range(range_min, range_max), weighted_mi)
+    plt.plot(range(range_min, range_max), mutual_informations)
+    plt.plot(max_x, max_y, 'rx')
+    plt.plot(range_min + mutual_informations.index(max(mutual_informations)), max(mutual_informations), 'rx')
+    plt.title('Mutual information of template slice registered to input slice.')
+    f.savefig(temp_dir + 'mutual_information.pdf')
+
+    # Run full (rigid, affine and nonlinear) registration on selected template slice.
+    # Transform segmentation accordingly.
+    full_reg_slice(slice_path, maximum_slice, output_path, maximum_slice_seg)
+
+    # Load segmentation, threshold, and close potential holes.
+    slice_data = nib.load(slice_path).get_data()
+    slice_seg_path = output_path.split('.nii')[0] + '_seg.nii'
+    slice_seg_nii = nib.load(slice_seg_path)
+    slice_seg_header = slice_seg_nii.header
+    slice_seg_data = slice_seg_nii.get_data()
+
+    # Calculate threshold.
+    threshold = threshold_otsu(slice_data)/6
+    foreground = np.where(slice_data > threshold, 1, 0)
+    foreground = morphology.binary_closing(foreground, selem=morphology.square(4))
+    slice_seg_data[foreground == 0] = 0
+
+    # Save threshold segmentation.
+    nib.save(nib.Nifti1Image(slice_seg_data, None, slice_seg_header), output_path.split('.nii')[0] + '_seg_thres.nii')
+    if return_result_dict:
+        return {'image': slice_path,
+                'segmentation': slice_seg_path,
+                'threshold_segmentation': output_path.split('.nii')[0] + '_seg_thres.nii',
+                'index': max_x,
+                'bregma': round((max_x - 38.7044)/9.7431, 2)}
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description='Register a 2 dimensional coronal brain slice to a template.')
+
+    parser.add_argument("-s", "--slice", required=True, metavar='\b', help="Existing path to the slice.")
+    parser.add_argument("-t", "--template", required=True, metavar='\b', help="Existing path to the template.")
+    parser.add_argument("-e", "--segmentation", required=True, metavar='\b', help="Existing path to the template segmentation.")
+    parser.add_argument("-o", "--output", required=True, metavar='\b', help="Output.")
+    parser.add_argument("-b", "--bregma", required=True, metavar='\b', help="Approximate bregma coordinate of slice.")
+    parser.add_argument("-p", "--preprocess", required=True, metavar='\b', help="""Pre-process input image. Includes
+    finding biggest connected component, normalizing and biasfieldcorrection.""")
+    parser.add_argument("-i", "--inverse", required=True, metavar='\b', help="""Path to input slice inversely
+    transformed to template space. Will not transform inversely if not set.""")
+
+    args = parser.parse_args()
+
+    main(args.slice, args.template, args.segmentation, args.output, args.bregma, args.preprocess, args.inverse)

+ 266 - 0
code_template_creation/template_creation.py

@@ -0,0 +1,266 @@
+from helper_functions import files_in_dir, make_dirs, add_slash_to_path, flip_volume_lr, robust_avg_volumes,\
+    files_ind_dir_wo_string, avg_template
+import sys
+import getopt
+import os
+import shutil
+from nipype.interfaces import ants
+
+
+# Help message.
+tab = '     '
+usage_message = '\nCOMMAND:\n' + tab + sys.argv[0] + \
+                '\n\nOPTIONS:\n' + \
+                tab + '-h, help menu\n' + \
+                tab + '-i, --images, <list of input images> or path to directory with only .nii images.\n' + \
+                tab + '-o, --output, <path to output directory>\n' + \
+                tab + '-s, --symmetric, [bool] use symmetric modelling. Default: False. Currently only works for 3d images.\n' + \
+                tab + '-t, --template, <path to template> can include template. Will not be included in final average. \n' + \
+                tab + '-w, --weight, [Double] specifies the weight a given template will have in averages. From 0 to 1. Default: 1/(number of images + 1)\n' + \
+                tab + '-l, --log, [bool]>\n'
+
+
+# Registration
+def registration(fixed, moving, result, reg_type, sigma, shrink, param, dim, initial_trans='', inverse_result=''):
+    """
+    Registration function. Registeres the moving image to the fixed with given parameters. If initial transform is not
+    given then the images are aligned by center of mass. Outputs inversely transformed result if given.
+    :param fixed: Path to fixed image.
+    :param moving: Path to moving image.
+    :param result: Path to result image.
+    :param reg_type: Registration type. List containing, Rigid, Affine, and/or SyN.
+    :param sigma: List of sigma parameters. Ordered according to the reg_type parameter.
+    :param shrink: List of shrink parameters. Ordered according to the reg_type parameter.
+    :param param: List of transform parameters. Ordered according to the reg_type parameter.
+    :param dim: Dimension of images. Typically 2 or 3.
+    :param initial_trans: Optional path to initial moving transform. If not given, the images will be matched initially
+    by aligning the center of mass.
+    :param inverse_result: Optional path to the inversely transformed fixed image.
+    :return: Returns nothing.
+    """
+    # Extract number of registrations.
+    steps = len(reg_type)
+
+    # Add 1000 iterations for each step.
+    if steps == 1:
+        iterations = [[1000] * len(sigma[0])]
+    else:
+        iterations = []
+        for i, reg in enumerate(reg_type):
+            iteration = [1000] * len(sigma[i])
+            iterations.append(iteration)
+
+    # Create registration instance.
+    reg_instance = ants.Registration(dimension=dim,
+                                     transforms=reg_type,
+                                     transform_parameters=param,
+                                     metric=['MI']*steps,
+                                     interpolation='BSpline',
+                                     fixed_image=[fixed],
+                                     moving_image=[moving],
+                                     metric_weight=[1]*steps,
+                                     radius_or_number_of_bins=[32]*steps,
+                                     number_of_iterations=iterations,
+                                     smoothing_sigmas=sigma,
+                                     shrink_factors=shrink,
+                                     convergence_threshold=[1.e-7],
+                                     sampling_strategy=['Regular']*steps,
+                                     use_histogram_matching=True,
+                                     winsorize_lower_quantile=0.05,
+                                     winsorize_upper_quantile=0.95,
+                                     output_warped_image=result,
+                                     write_composite_transform=True,
+                                     output_transform_prefix=result.split('.')[0] + '-Transformation',
+                                     num_threads=12)
+    # Add initial moving transform if given, else match by center of mass.
+    if not initial_trans == '':
+        reg_instance.inputs.initial_moving_transform = initial_trans
+    else:
+        reg_instance.inputs.initial_moving_transform_com = 1
+
+    # Output reverse results if path is given.
+    if not inverse_result == '':
+        reg_instance.inputs.output_inverse_warped_image = inverse_result
+
+    # Run registration.
+    reg_instance.run()
+
+
+def create_template_from_images(images_input, results_dir, symmetric=False, template='', template_weight=1,
+                                r=3, a=2, nl=3, print_log=False):
+    """
+    Population based template creation from input images. If input template is given this will be part of the averaging
+    with weight template_weight. Will perform r rigid, a rigid and affine, and nl rigid, affine, and non-linear
+    iterations. Will flip all input images in the left-right plane if symmetric is set to true.
+    :param images_input: List of paths to images.
+    :param results_dir: Working directory.
+    :param symmetric: Boolean [Default: False]. Will flip all input images in the left-right plane if symmetric is set
+    to true.
+    :param template: str [Default: '']. Will include template in averaging if path is specified.
+    :param template_weight: float [Default: 1]. Will weight the template with given weight if set to different from 1.
+    Can be in the range of 0 < template_weight < 1.
+    :param r: Int [Default: 3]. Number of rigid registrations.
+    :param a: Int [Default: 2]. Number of rigid and affine registrations.
+    :param nl: Int [Default: 3]. Number of rigid, affine, and non-linear registrations.
+    :param print_log: Boolean [Default: False]. Will print log if true.
+    :return: Returns nothing.
+    """
+    out_dir = add_slash_to_path(results_dir)
+    make_dirs(out_dir)
+    template_present = not template == ''
+
+    if print_log:
+        print('Determining images-variable format and defining input images.')
+    if isinstance(images_input, list):
+        images = images_input
+    elif isinstance(images_input, str):
+        images_input = add_slash_to_path(images_input)
+        images = files_in_dir(images_input, '.nii')
+
+    all_images = images
+
+    if symmetric:
+        flipped_path = out_dir + 'raw_flipped/'
+        flipped_images = []
+        make_dirs(flipped_path)
+        if print_log:
+            print('Creating flipped images.')
+        for image in images:
+            flipped_image = flipped_path + image.split('/')[-1].split('.')[0] + '_flipped.nii'
+            if not os.path.exists(flipped_image):
+                flip_volume_lr(image, flipped_image)
+            else:
+                if print_log:
+                    print('Flipped image', flipped_image, 'already present.')
+            flipped_images.append(flipped_image)
+
+        all_images.extend(flipped_images)
+
+    if print_log:
+        print('Defining iterations.')
+    reg_types = ['Rigid'] * r + ['Affine'] * a + ['SyN'] * nl
+
+    if print_log:
+        print(reg_types)
+
+    for i, iteration in enumerate(reg_types):
+        iteration_nr = str(i).zfill(2)
+        cur_it_dir = out_dir + iteration_nr + '/'
+        make_dirs(cur_it_dir)
+
+        if i == 0:
+            robust_avg_volumes(all_images, out_dir + '00-average.nii')
+            if template_present:
+                avg_template(out_dir + '00-average.nii',
+                             template,
+                             template_weight,
+                             out_dir + '00-average-template.nii')
+        else:
+            shutil.rmtree(out_dir + str(i-1).zfill(2))
+
+        average = out_dir + iteration_nr + '-average-template.nii' if template_present else out_dir + iteration_nr + '-average.nii'
+
+        # Iteration specific registration parameters.
+        if iteration == 'Rigid':
+            reg = ['Rigid']
+            params = [(0.25,)]
+            sigma = [[4, 4, 2, 2]]
+            shrink = [[32, 16, 8, 4]]
+            if i == 0:
+                sigma = [[4, 4, 4]]
+                shrink = [[32, 16, 8]]
+        elif iteration == 'Affine':
+            reg = ['Rigid', 'Affine']
+            params = [(0.25,), (0.25,), ]
+            sigma = [[4, 4, 2, 2], [4, 4, 2, 2]]
+            shrink = [[32, 16, 8, 4], [32, 16, 8, 4]]
+        elif iteration == 'SyN':
+            reg = ['Rigid', 'Affine', 'SyN']
+            params = [(0.25,), (0.25,), (0.15, 3, 0.5)]
+            sigma = [[4, 4, 2, 2], [4, 4, 2, 2], [4, 4, 4, 2]]
+            shrink = [[32, 16, 8, 4], [32, 16, 8, 4], [64, 32, 16, 8]]
+            if i == len(reg_types) - 1:
+                sigma = [[4, 4, 2, 2], [4, 4, 2, 2], [4, 4, 4, 2, 1]]
+                shrink = [[32, 16, 8, 4], [32, 16, 8, 4], [64, 32, 16, 8, 4]]
+        else:
+            sys.exit('The registration type "' + str(iteration) + '" does not exist. Reconstruction stopped.')
+
+        if template_present:
+            all_images.extend([template])
+
+        for image in all_images:
+            reg_output = cur_it_dir + image.split('/')[-1]
+            registration(average, image, reg_output, reg, sigma, shrink, params, dim=3)
+
+        registered_images = files_ind_dir_wo_string(cur_it_dir, '-Transformation')
+        registered_images_wo_allen = []
+
+        for reg_image in registered_images:
+            if 'allen' in reg_image:
+                registered_allen = reg_image
+            else:
+                registered_images_wo_allen.append(reg_image)
+
+        robust_avg_volumes(registered_images_wo_allen, out_dir + str(i+1).zfill(2) + '-average.nii')
+        if template_present:
+            avg_template(out_dir + str(i+1).zfill(2) + '-average.nii',
+                         registered_allen,
+                         template_weight,
+                         out_dir + str(i + 1).zfill(2) + '-average-template.nii')
+
+
+if __name__ == '__main__':
+    arguments = sys.argv
+
+    input_symmetric = False
+    input_robust = True
+    input_template = ''
+    input_weight = 1
+    show_log = False
+
+    if len(arguments) == 1:
+        print(usage_message)
+    else:
+        try:
+            opts, args = getopt.getopt(arguments[1:], "hi:o:s:t:w:l:", ["images=", "output=", "symmetric=",
+                                                                        "template=", "weight=", "log="])
+        except getopt.GetoptError:
+            print('\nSomething is not right in your syntax. Tak a look at your possibilities:\n', usage_message)
+            sys.exit(2)
+        for opt, arg in opts:
+            if opt == "-h":
+                print(usage_message)
+                sys.exit()
+            elif opt in ("-i", "--images"):
+                input_images = arg
+            elif opt in ("-o", "--output"):
+                output_directory = arg
+            elif opt in ("-s", "--symmetric"):
+                if arg == 'True' or arg == 'true':
+                    input_symmetric = True
+                elif arg == 'False' or arg == 'false':
+                    input_symmetric = False
+                else:
+                    print('\nSomething is not right in your syntax. Tak a look at your possibilities:\n', usage_message)
+            elif opt in ("-t", "--template"):
+                input_template = arg
+            elif opt in ("-w", "--weight"):
+                input_weight = arg
+            elif opt in ("-l", "--log"):
+                if arg == 'True' or arg == 'true':
+                    show_log = True
+                elif arg == 'False' or arg == 'false':
+                    show_log = False
+                else:
+                    print('\nSomething is not right in your syntax. Tak a look at your possibilities:\n', usage_message)
+                    sys.exit(2)
+        try:
+            create_template_from_images(input_images,
+                                        output_directory,
+                                        symmetric=input_symmetric,
+                                        template=input_template,
+                                        template_weight=input_weight,
+                                        print_log=show_log)
+        except NameError:
+            print('\nYou are not (properly) defining all the input variables. Tak a look at what is needed:\n',
+                  usage_message)

+ 0 - 0
data/mice/preprocessed/data.txt


+ 1 - 0
data/mice/tif/BM10/BM10_S01.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/PW/5X/SHA256E-s125128145--28325bd31fb6fd7031924811c66cf49442ad1783001d0d0b5981c6ee257067ed.tif/SHA256E-s125128145--28325bd31fb6fd7031924811c66cf49442ad1783001d0d0b5981c6ee257067ed.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S02.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/3f/jq/SHA256E-s125128146--4ca87ccc28550306b73220dee50d3cfbd3143c7dd93f8a454daed4223611a439.tif/SHA256E-s125128146--4ca87ccc28550306b73220dee50d3cfbd3143c7dd93f8a454daed4223611a439.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S03.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Wg/gk/SHA256E-s125128146--0aa1320ecac6f3b002334199cf77bab597c5d418523b09bc6d4c8c29530bc4f7.tif/SHA256E-s125128146--0aa1320ecac6f3b002334199cf77bab597c5d418523b09bc6d4c8c29530bc4f7.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S04.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/kG/Qf/SHA256E-s125128147--bb51cb7e7c1abe7c0516d7a1177128afc4220a6fba9d685b73602ff5e81ab985.tif/SHA256E-s125128147--bb51cb7e7c1abe7c0516d7a1177128afc4220a6fba9d685b73602ff5e81ab985.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S05.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/6P/kM/SHA256E-s125128147--8db2d92e7221bebf1e15f2062dadd463719b6b90433f4bc4ee7fd39c970ced91.tif/SHA256E-s125128147--8db2d92e7221bebf1e15f2062dadd463719b6b90433f4bc4ee7fd39c970ced91.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S06.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/wp/Jm/SHA256E-s125128147--20aecf44a6e97e03ddfea4dcd3ce896afd54f16cff45e36a1d82e6f10f22ce40.tif/SHA256E-s125128147--20aecf44a6e97e03ddfea4dcd3ce896afd54f16cff45e36a1d82e6f10f22ce40.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S07.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/JW/gM/SHA256E-s125128147--261b3f3f037f3c96ec717ec2753df4b3735bda686273a5643e94311fbda7f25d.tif/SHA256E-s125128147--261b3f3f037f3c96ec717ec2753df4b3735bda686273a5643e94311fbda7f25d.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S08.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/w8/pv/SHA256E-s125128147--2291066b518c2307b58c17fb5355e6bead93e5f2d3d12160ff112aaf46dae89b.tif/SHA256E-s125128147--2291066b518c2307b58c17fb5355e6bead93e5f2d3d12160ff112aaf46dae89b.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S09.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/X2/fM/SHA256E-s125128147--5680b5049abd52757bcb075bc52a1039f965007a17f7308ba781c9b0804ee936.tif/SHA256E-s125128147--5680b5049abd52757bcb075bc52a1039f965007a17f7308ba781c9b0804ee936.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S10.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Ff/M1/SHA256E-s125128147--cfe2ca0222fffd336ca28ac24d1687a9c03a3ca05b452c496f9bb3443254ba37.tif/SHA256E-s125128147--cfe2ca0222fffd336ca28ac24d1687a9c03a3ca05b452c496f9bb3443254ba37.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S11.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/2j/4f/SHA256E-s125128147--2d1719f16819b25a9010778b996969746947d6ca59e5ec67bd1b366767021ba0.tif/SHA256E-s125128147--2d1719f16819b25a9010778b996969746947d6ca59e5ec67bd1b366767021ba0.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S12.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/qG/pW/SHA256E-s125128147--5c1707ca8466366216ba10416a3f2cebd61460989f79841cf39c01128bfb632c.tif/SHA256E-s125128147--5c1707ca8466366216ba10416a3f2cebd61460989f79841cf39c01128bfb632c.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S13.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Gj/X2/SHA256E-s125128147--b031faed3e455bef8d5490ad70615940f4c87b7e5cce6252cc3d5ac9e9998a02.tif/SHA256E-s125128147--b031faed3e455bef8d5490ad70615940f4c87b7e5cce6252cc3d5ac9e9998a02.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S14.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/49/4M/SHA256E-s125128147--b94eba10c18f623fce7ffa11ccdeed39cd4a1ecfa2cee89b326dc1f0a04d24a9.tif/SHA256E-s125128147--b94eba10c18f623fce7ffa11ccdeed39cd4a1ecfa2cee89b326dc1f0a04d24a9.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S15.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/qw/fZ/SHA256E-s125128147--209f33178692d37a6249783cd794418c061c67acedb046fdc5e8afdfabdaeb09.tif/SHA256E-s125128147--209f33178692d37a6249783cd794418c061c67acedb046fdc5e8afdfabdaeb09.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S16.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/w3/6v/SHA256E-s125128147--093fcb692f810c9997231856430b56e70d8371d2c43e5ae6868db7cc444dbc65.tif/SHA256E-s125128147--093fcb692f810c9997231856430b56e70d8371d2c43e5ae6868db7cc444dbc65.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S17.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/jp/pG/SHA256E-s125128149--ac4b360b4fb8d4e31d9a9fc41f2b30658a0bc0a4e2dc552c8027ac9152a3dd44.tif/SHA256E-s125128149--ac4b360b4fb8d4e31d9a9fc41f2b30658a0bc0a4e2dc552c8027ac9152a3dd44.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S18.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/4f/0P/SHA256E-s125128150--3d6378ceee5414466bd028c0c4f0b2faf98836c9205146ab0644f293cb30f034.tif/SHA256E-s125128150--3d6378ceee5414466bd028c0c4f0b2faf98836c9205146ab0644f293cb30f034.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S19.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/jZ/q0/SHA256E-s125128151--d6b07f006be62f5113e5fd377062102a376c3d7908b50135c931ee0bd71890e8.tif/SHA256E-s125128151--d6b07f006be62f5113e5fd377062102a376c3d7908b50135c931ee0bd71890e8.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S20.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/mQ/4v/SHA256E-s125128151--58235c17f8e290e55f7c709ab015827012ac70c7e59fbd59fae321386ca550b1.tif/SHA256E-s125128151--58235c17f8e290e55f7c709ab015827012ac70c7e59fbd59fae321386ca550b1.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S21.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/w6/mZ/SHA256E-s125128151--f418a0a42b99f2e386004f13a9fd2134d6206ac76be77d9585ecf13ac572bda8.tif/SHA256E-s125128151--f418a0a42b99f2e386004f13a9fd2134d6206ac76be77d9585ecf13ac572bda8.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S22.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/pg/44/SHA256E-s125128151--fd717cf3501e924d683d21dc8de35c166dfcac4532b90aadf94c6cf81a98383a.tif/SHA256E-s125128151--fd717cf3501e924d683d21dc8de35c166dfcac4532b90aadf94c6cf81a98383a.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S23.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/mf/9X/SHA256E-s125128151--1043a597071e7323211409e9ce6c59cadee5c041a2fc1fcac09fbf8eb4228553.tif/SHA256E-s125128151--1043a597071e7323211409e9ce6c59cadee5c041a2fc1fcac09fbf8eb4228553.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S24.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/wk/6X/SHA256E-s125128151--81be174a32a1538719161fd2c613c0f45cf1c0a49a20e58006c393bd80fcad21.tif/SHA256E-s125128151--81be174a32a1538719161fd2c613c0f45cf1c0a49a20e58006c393bd80fcad21.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S25.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/6w/3P/SHA256E-s125128151--0b7a8e04250e2ed8fb60ae461ac18ab20f963fcaf8df6783399376cbfb63dc1d.tif/SHA256E-s125128151--0b7a8e04250e2ed8fb60ae461ac18ab20f963fcaf8df6783399376cbfb63dc1d.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S26.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Jx/g8/SHA256E-s125128151--ab7fe96a98b21f54fba5e052df01d8307d866dd32aba0e8f6f9db6dd246a952d.tif/SHA256E-s125128151--ab7fe96a98b21f54fba5e052df01d8307d866dd32aba0e8f6f9db6dd246a952d.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S27.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/M3/pf/SHA256E-s125128151--1b1302fdeca64c7ca329e77cb14cd05d351e6525c743db7becff793cc9da932c.tif/SHA256E-s125128151--1b1302fdeca64c7ca329e77cb14cd05d351e6525c743db7becff793cc9da932c.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S28.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/PJ/Ff/SHA256E-s125128151--a562d9e10cf58e36c9b1e6d0bd842f686084a4ec68aec32714ad1495a8a1b8d7.tif/SHA256E-s125128151--a562d9e10cf58e36c9b1e6d0bd842f686084a4ec68aec32714ad1495a8a1b8d7.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S29.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/gx/8V/SHA256E-s125128151--b88e13868abf1d86d1df241a129cd5e6e098456af65e5297f5b411f5c5820ee0.tif/SHA256E-s125128151--b88e13868abf1d86d1df241a129cd5e6e098456af65e5297f5b411f5c5820ee0.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S30.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/fJ/5P/SHA256E-s125128151--84cc5161064bedf3b8f83ded4780a6b834161d48d59c6f49e490bb2031b1ca50.tif/SHA256E-s125128151--84cc5161064bedf3b8f83ded4780a6b834161d48d59c6f49e490bb2031b1ca50.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S31.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/8M/4J/SHA256E-s125128152--bbc5339c0840556b595cf3f4fc9a8875b586f0ae29ca944e87b080be962a6471.tif/SHA256E-s125128152--bbc5339c0840556b595cf3f4fc9a8875b586f0ae29ca944e87b080be962a6471.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S32.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/06/W0/SHA256E-s125128152--4ec69b8c565cf52df212d4a897e9028f5a7caf1ac834cb413d8dea36ea3449b1.tif/SHA256E-s125128152--4ec69b8c565cf52df212d4a897e9028f5a7caf1ac834cb413d8dea36ea3449b1.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S33.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/jx/Kq/SHA256E-s125128149--bb6b553279fc058c7c471ed7949672d31bd7aabd048078c357e6197cd227bbc0.tif/SHA256E-s125128149--bb6b553279fc058c7c471ed7949672d31bd7aabd048078c357e6197cd227bbc0.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S34.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/zM/K5/SHA256E-s125128150--733acb65e7d833fcb25e1d817bb22cdd7bb90fbdad91799c5ae19c6d47e05a3b.tif/SHA256E-s125128150--733acb65e7d833fcb25e1d817bb22cdd7bb90fbdad91799c5ae19c6d47e05a3b.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S35.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/V2/JK/SHA256E-s125128151--968fe0253ce47399fc50654e5a93dd3a64c6b08ee52887245932feaebc4fb9a1.tif/SHA256E-s125128151--968fe0253ce47399fc50654e5a93dd3a64c6b08ee52887245932feaebc4fb9a1.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S36.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/xK/XF/SHA256E-s125128151--c58651e90e838b6ae92c271c0ce85c1601863bee22578d1a18700704f2b49d5f.tif/SHA256E-s125128151--c58651e90e838b6ae92c271c0ce85c1601863bee22578d1a18700704f2b49d5f.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S37.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/zz/FM/SHA256E-s125128151--baa3ac3e62e2c9bc661985345d2b80307a4eaa6df96a8b948a5c956ed1f97cd1.tif/SHA256E-s125128151--baa3ac3e62e2c9bc661985345d2b80307a4eaa6df96a8b948a5c956ed1f97cd1.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S38.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Qm/9W/SHA256E-s125128151--51c0ce2b96e3a905b661ca0035c4668a0d0813746a6633f01a421dfbb91b652c.tif/SHA256E-s125128151--51c0ce2b96e3a905b661ca0035c4668a0d0813746a6633f01a421dfbb91b652c.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S39.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/wX/p2/SHA256E-s125128151--e4e7e25ec9e08c2c4431167f445c202b7ccf92946c1f2d22fb6217431c055538.tif/SHA256E-s125128151--e4e7e25ec9e08c2c4431167f445c202b7ccf92946c1f2d22fb6217431c055538.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S40.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/3W/q8/SHA256E-s125128151--00cde2ad133aa60fa6c9d19246269305c0574bf4cc72e86be3257feb469878f6.tif/SHA256E-s125128151--00cde2ad133aa60fa6c9d19246269305c0574bf4cc72e86be3257feb469878f6.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S41.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/5p/WP/SHA256E-s125128151--4f243ac85318c7a278f9ee1f9275ffc53453af0343be5927e617d0b640b6818d.tif/SHA256E-s125128151--4f243ac85318c7a278f9ee1f9275ffc53453af0343be5927e617d0b640b6818d.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S42.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/x7/V8/SHA256E-s125128151--fc1e6e697328bdeb5e010282eabb5519c2592d7a0f86b0075b75e58af8eb177a.tif/SHA256E-s125128151--fc1e6e697328bdeb5e010282eabb5519c2592d7a0f86b0075b75e58af8eb177a.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S43.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/xW/pZ/SHA256E-s125128151--3cfb85643495f713b546c7d1b3bf08d5dc191c94822ea38150eb8291bf168f34.tif/SHA256E-s125128151--3cfb85643495f713b546c7d1b3bf08d5dc191c94822ea38150eb8291bf168f34.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S44.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/j6/km/SHA256E-s125128151--6608d7dfb3c09335908e3c6aacaee8488f1b088a636eba4c343277c02f6d88d3.tif/SHA256E-s125128151--6608d7dfb3c09335908e3c6aacaee8488f1b088a636eba4c343277c02f6d88d3.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S45.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/J4/24/SHA256E-s125128151--427202c47c214113f5e8ee3af4d0c0d8f10fb6e5632072908dd23f4c2bcb221d.tif/SHA256E-s125128151--427202c47c214113f5e8ee3af4d0c0d8f10fb6e5632072908dd23f4c2bcb221d.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S46.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/PQ/m7/SHA256E-s125128151--1c1cc770c3c5254b942d9aad0a94f6e64b1c1c1a1068b63fd8d965c1dfa9c1f4.tif/SHA256E-s125128151--1c1cc770c3c5254b942d9aad0a94f6e64b1c1c1a1068b63fd8d965c1dfa9c1f4.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S47.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/K8/0Z/SHA256E-s125128152--6450ffcfa2c50229d4a773204933e46c7d2eb71401da8e2d92c36b9348592995.tif/SHA256E-s125128152--6450ffcfa2c50229d4a773204933e46c7d2eb71401da8e2d92c36b9348592995.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S48.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Kw/k4/SHA256E-s125128152--a19b5a9245f0ca8a9e32ebfc761715366f61ff3cb9347764bed3b383ee7146c3.tif/SHA256E-s125128152--a19b5a9245f0ca8a9e32ebfc761715366f61ff3cb9347764bed3b383ee7146c3.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S49.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Qj/XX/SHA256E-s125128149--30c308536c5bf2d41b6228ecdc78e9635d257b5ee647c80caea42dc6fe637fd0.tif/SHA256E-s125128149--30c308536c5bf2d41b6228ecdc78e9635d257b5ee647c80caea42dc6fe637fd0.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S50.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/FV/JQ/SHA256E-s125128150--4c1ece8c1e1e2da602c7ed0eaa5965dc2232cce8f41043e9963e9fc53d9611e8.tif/SHA256E-s125128150--4c1ece8c1e1e2da602c7ed0eaa5965dc2232cce8f41043e9963e9fc53d9611e8.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S51.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/zV/1m/SHA256E-s125128151--d0381ed39d999af662e6a7db59bf51e6c5ba98dbdc1d46b6c5faeb9117a621b4.tif/SHA256E-s125128151--d0381ed39d999af662e6a7db59bf51e6c5ba98dbdc1d46b6c5faeb9117a621b4.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S52.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/6k/jV/SHA256E-s125128151--42fc6deae8f3899150bb3dc6a6e7bc232653bdbc457574a0d9fb83b11bd2fd4a.tif/SHA256E-s125128151--42fc6deae8f3899150bb3dc6a6e7bc232653bdbc457574a0d9fb83b11bd2fd4a.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S53.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/wk/8Q/SHA256E-s125128151--be632b52d318dce44c8aab2547a0c19dcc61786d26d295c82cdcc8a602956d1a.tif/SHA256E-s125128151--be632b52d318dce44c8aab2547a0c19dcc61786d26d295c82cdcc8a602956d1a.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S54.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/JW/wK/SHA256E-s125128151--ef98300531e459460ccc835bf38041e1abc808fe47d637313f376e5cfbad4121.tif/SHA256E-s125128151--ef98300531e459460ccc835bf38041e1abc808fe47d637313f376e5cfbad4121.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S55.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/4f/fq/SHA256E-s125128151--3fdb504be005987cc2bc705cab28693dd2a2120b73e3005f60d16c8c3687e9b6.tif/SHA256E-s125128151--3fdb504be005987cc2bc705cab28693dd2a2120b73e3005f60d16c8c3687e9b6.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S56.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/4f/Q7/SHA256E-s125128151--8c4c6d9f3354fd90979d666e6df32967895a86a6a7d21c453092802f608513d5.tif/SHA256E-s125128151--8c4c6d9f3354fd90979d666e6df32967895a86a6a7d21c453092802f608513d5.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S57.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/zz/P5/SHA256E-s125128151--b9540aa4e84666b2e51ec524530503919ffaa4dea3d1cc452dc628e882084a46.tif/SHA256E-s125128151--b9540aa4e84666b2e51ec524530503919ffaa4dea3d1cc452dc628e882084a46.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S58.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/3V/2f/SHA256E-s125128151--e40c20794a31f74a3d498e7dca51b6770dafe5ff216112034a9b7b40d0e634cb.tif/SHA256E-s125128151--e40c20794a31f74a3d498e7dca51b6770dafe5ff216112034a9b7b40d0e634cb.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S59.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Gw/Wp/SHA256E-s125128151--213513aa2e98807d9428eaa402785cdef084515940bc3979bdb7840aff01343a.tif/SHA256E-s125128151--213513aa2e98807d9428eaa402785cdef084515940bc3979bdb7840aff01343a.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S60.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Zk/wp/SHA256E-s125128151--2dc6b0beaa60a68cba8d4b7bd5a279cb96dafcc606974ec6734af5ded180bae4.tif/SHA256E-s125128151--2dc6b0beaa60a68cba8d4b7bd5a279cb96dafcc606974ec6734af5ded180bae4.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S61.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/pF/1q/SHA256E-s125128151--907c79933f056058deac6ea703fbb0d66d4af7af0e77fa9d2c039a88c772db45.tif/SHA256E-s125128151--907c79933f056058deac6ea703fbb0d66d4af7af0e77fa9d2c039a88c772db45.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S62.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/Zg/xw/SHA256E-s125128152--a45ae1d7bcc1189f85abefb4437a83c1e6c95b00adcf804ac555808d687ee3fc.tif/SHA256E-s125128152--a45ae1d7bcc1189f85abefb4437a83c1e6c95b00adcf804ac555808d687ee3fc.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S63.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/0p/k5/SHA256E-s125128152--a6275dcc9a5bebeb8def1ac469b6cfe179faf940ab6e15a1d7a3fe7accae9045.tif/SHA256E-s125128152--a6275dcc9a5bebeb8def1ac469b6cfe179faf940ab6e15a1d7a3fe7accae9045.tif

+ 1 - 0
data/mice/tif/BM10/BM10_S64.tif

@@ -0,0 +1 @@
+../../../../.git/annex/objects/ZF/9p/SHA256E-s125128152--451283535ca5885237114a577bbe468a6479246ea95d3712916b8b3da0c657a7.tif/SHA256E-s125128152--451283535ca5885237114a577bbe468a6479246ea95d3712916b8b3da0c657a7.tif