Source code for ClearMap.Alignment.Elastix

# -*- coding: utf-8 -*-
"""
Elastix
=======

Interface to Elastix for alignment of volumetric data.

The elastix documentation can be found `here <http://elastix.isi.uu.nl/>`_.

In essence, a transformation :math:`T(x)` is sought so that for a fixed image 
:math:`F(x)` and a moving image :math:`M(x)`:

.. math::
    F(x) = M(T(x))

Once the map :math:`T` is estimated via elastix, transformix maps an image
:math:`I(x)` from the moving image frame to the fixed image frame, i.e.:

.. math::
    I(x) \\rightarrow I(T(x)) 

To register an image onto a reference image, the fixed image is typically 
choosed to be the image to be registered, while the moving image is the 
reference image. In this way an object identified in the data at position x
is mapped via transformix as:

.. math::
    x \\rightarrow T(x)

Summary
-------
    * elastix finds a transformation :math:`T: \\mathrm{fixed image} \\rightarrow \\mathrm{moving image}`
    * the fixed image is image to be registered
    * the moving image is typically the reference image
    * the result folder may contain an image (mhd file) that is :math:`T^{-1}(\\mathrm{moving})`,
      i.e. has the size of the fixed image
    * transformix applied to data gives :math:`T^{-1}(\\mathrm{data})` !
    * transformix applied to points gives :math:`T(\\mathrm{points})` !
    * point arrays are assumed to be in (x,y,z) coordinates consistent with (x,y,z) array represenation of images in ClearMap
    
Main routines are: :func:`align`, :func:`transform`.
    
See Also
--------
    `Elastix documentation <http://elastix.isi.uu.nl/>`_
    :mod:`~ClearMap.Alignment.Resampling`
"""
__author__    = 'Christoph Kirst <christoph.kirst.ck@gmail.com>'
__license__   = 'GPLv3 - GNU General Pulic License v3 (see LICENSE)'
__copyright__ = 'Copyright © 2020 by Christoph Kirst'
__webpage__   = 'http://idisco.info'
__download__  = 'http://www.github.com/ChristophKirst/ClearMap2'


import os
import tempfile
import shutil
import re

import numpy as np

import multiprocessing as mp

import ClearMap.IO.IO as io
import ClearMap.Settings as settings


##############################################################################
### Initialization and Settings
##############################################################################

elastix_binary = None;
"""The elastix executable.

Notes
-----
  - setup in :func:`initialize_elastix`
"""

elastix_lib = None;
"""Path to the elastix library.

Notes
-----
  - setup in :func:`initialize_elastix`
"""

transformix_binary = None;
"""The transformix executable.

Notes
-----
  - setup in :func:`initialize_elastix`
"""
    
initialized = False;
"""Status of the elastixs binarys and paths.

Notes
-----
  - setup in :func:`initialize_elastix`
"""

    



[docs]def set_elastix_library_path(path = None): """Add elastix library path to the LD_LIBRARY_PATH variable in linux. Arguments --------- path : str or None Path to elastix root directory. If None :const:`ClearMap.Settings.elastix_path` is used. """ if path is None: path = settings.elastix_path; if 'LD_LIBRARY_PATH' in os.environ.keys(): lp = os.environ['LD_LIBRARY_PATH']; if not path in lp.split(':'): os.environ['LD_LIBRARY_PATH'] = lp + ':' + path; else: os.environ['LD_LIBRARY_PATH'] = path
[docs]def initialize_elastix(path = None): """Initialize all paths and binaries of elastix Arguments --------- path (str or None): path to elastix root directory, if None :const:`ClearMap.Settings.elastix_path` is used. See also -------- :const:`elastix_binary`, :const:`elastix_lib`, :const:`transformix_binary`, :const:`initialized`, :func:`set_elastix_library_path` """ global elastix_binary, elastix_lib, transformix_binary, initialized if path is None: path = settings.elastix_path; if path is None: raise RuntimeError("Cannot find elastix path!"); #search for elastix binary elastixbin = os.path.join(path, 'bin/elastix'); if os.path.exists(elastixbin): elastix_binary = elastixbin; else: raise RuntimeError("Cannot find elastix binary %s, set path in Settings.py accordingly!" % elastixbin); #search for transformix binarx transformixbin = os.path.join(path, 'bin/transformix'); if os.path.exists(transformixbin): transformix_binary = transformixbin; else: raise RuntimeError("Cannot find transformix binary %s set path in Settings.py accordingly!" % transformixbin); #search for elastix libs elastix_lib = os.path.join(path, 'lib'); if os.path.exists(elastix_lib): elastix_lib = elastix_lib; else: elastix_lib = os.path.join(path, 'bin'); if os.path.exists(elastix_lib): elastix_lib = elastix_lib; else: raise RuntimeError("Cannot find elastix libs in %s set path in Settings.py accordingly!" % elastix_lib); #set path set_elastix_library_path(elastix_lib); initialized = True; print("Elastix sucessfully initialized from path: %s" % path); return path;
initialize_elastix();
[docs]def check_elastix_initialized(): """Checks if elastix is initialized Returns ------- initialized : bool True if elastix paths are set. """ if not initialized: raise RuntimeError("Elastix not initialized: run initialize_elastix(path) with proper path to elastix first"); return True;
############################################################################### ### Basic interface routines ####################################txtfile####################################
[docs]def transform_file(result_directory): """Finds and returns the transformation parameter file. Arguments --------- result_directory : str Path to directory of elastix results. Returns ------- filename : str File name of the first transformation parameter file. Notes ----- In case of multiple transformation parameter files the top level file is returned. """ files = os.listdir(result_directory); files = [x for x in files if re.match('TransformParameters.\d.txt', x)]; files.sort(); if files == []: raise RuntimeError('Cannot find a valid transformation file in %r!' % result_directory); return os.path.join(result_directory, files[-1])
[docs]def transform_directory_and_file(transform_parameter_file = None, transform_directory = None): """Determines transformation directory and file from either. Arguments --------- transform_parameter_file : str or None File name of the transformation parameter file. transform_directory : str or None Directory to the transformation parameter. Returns ------- transform_parameter_file : str File name of the transformation parameter file. transform_directory : str Directory to the transformation parameter. Notes ----- Only one of the two arguments need to be specified. """ if transform_parameter_file == None: if transform_directory == None: raise ValueError('Neither the alignment directory nor the transformation parameter file is specified!'); transform_parameter_dir = transform_directory transform_parameter_file = transform_file(transform_parameter_dir); else: transform_parameter_dir = os.path.split(transform_parameter_file); transform_parameter_dir = transform_parameter_dir[0]; transform_parameter_file = transform_parameter_file; return transform_parameter_dir, transform_parameter_file;
[docs]def set_path_transform_files(result_directory): """Replaces relative with absolute path in the parameter files in the result directory. Arguments --------- result_directory : str Path to directory of elastix results. Notes ----- When elastix is not run in the directory of the transformation files the aboslute path needs to be given in each transformation file to point to the subsequent transformation files. This is done via this routine. """ files = os.listdir(result_directory); files = [x for x in files if re.match('TransformParameters.\d.txt', x)]; files.sort(); if files == []: raise RuntimeError('Cannot find a valid transformation file in %r!' % result_directory); rec = re.compile("\(InitialTransformParametersFileName \"(?P<parname>.*)\"\)"); for f in files: fh, tmpfn = tempfile.mkstemp(); ff = os.path.join(result_directory, f); #print ff with open(tmpfn, 'w') as newfile: with open(ff) as parfile: for line in parfile: #print line m = rec.match(line); if m != None: pn = m.group('parname'); if pn != 'NoInitialTransform': pathn, filen = os.path.split(pn); filen = os.path.join(result_directory, filen); newfile.write(line.replace(pn, filen)); else: newfile.write(line); else: newfile.write(line); os.close(fh); os.remove(ff); shutil.move(tmpfn, ff);
[docs]def set_metric_parameter_file(parameter_file, metric): """Replaces the metric in the parameter file. Arguments --------- parameter_file : str The parameter file name. metric : str The metric to use. Notes ----- Used to replace the metric when inverse transform is estimated. """ fh, tmpfn = tempfile.mkstemp(); rec = re.compile("\(Metric \"(?P<parname>.*)\"\)"); mset = False; with open(tmpfn, 'w') as newfile: with open(parameter_file) as parfile: for line in parfile: #print line m = rec.match(line); if m != None: pn = m.group('parname'); newfile.write(line.replace(pn, metric)); mset = True; else: newfile.write(line); if not mset: newfile.write("(Metric \"" + metric + "\")\n"); os.close(fh); os.remove(parameter_file); shutil.move(tmpfn, parameter_file);
[docs]def result_data_file(result_directory): """Returns the mhd result file in a result directory. Arguments --------- result_directory : str Path to elastix result directory. Returns ------- result_file : str The mhd file in the result directory. """ files = os.listdir(result_directory); files = [x for x in files if re.match('.*.mhd', x)]; files.sort(); if files == []: raise RuntimeError('Cannot find a valid result data file in ' + result_directory); return os.path.join(result_directory, files[0])
[docs]def transform_file_size_and_spacing(transform_file): """Parse the image size and spacing from a transformation parameter file. Arguments --------- transform_file : str File name of the transformix parameter file. Returns ------- size: tuple The image size. spacing : tuple The image spacing. """ resi = re.compile("\(Size (?P<size>.*)\)"); resp = re.compile("\(Spacing (?P<spacing>.*)\)"); si = None; sp = None; with open(transform_file) as parfile: for line in parfile: m = resi.match(line) if m != None: pn = m.group('size'); si = pn.split(); m = resp.match(line); if m != None: pn = m.group('spacing'); sp = pn.split(); parfile.close(); si = [float(x) for x in si]; sp = [float(x) for x in sp]; return si, sp
[docs]def set_transform_file_size_and_spacing(transform_file, size, spacing): """Replaces size and scale in the transformation parameter file Arguments --------- transform_file : str Transformation parameter file. size : tuple The new image size. spacing : tuple The new image spacing. """ resi = re.compile("\(Size (?P<size>.*)\)"); resp = re.compile("\(Spacing (?P<spacing>.*)\)"); fh, tmpfn = tempfile.mkstemp(); si = [int(x) for x in size]; with open(transform_file) as parfile: with open(tmpfn, 'w') as newfile: for line in parfile: m = resi.match(line) if m != None: newfile.write("(Size %d %d %d)" % si); else: m = resp.match(line) if m != None: newfile.write("(Spacing %d %d %d)" % spacing); else: newfile.write(line); newfile.close(); parfile.close(); os.remove(transform_file); shutil.move(tmpfn, transform_file);
[docs]def rescale_size_and_spacing(size, spacing, scale): """Rescales the size and spacing Arguments --------- size : tuple The image size. spacing : tuple The image spacing. scale : tuple The scale factor. Returns ------- size : tuple The scaled image size. spacing : tuple The scaled image spacing. """ si = [int(x * scale) for x in size]; sp = spacing / scale; return si, sp
############################################################################## ### Elastix Runs ##############################################################################
[docs]def align(fixed_image, moving_image, affine_parameter_file, bspline_parameter_file = None, result_directory = None, processes = None): """Align images using elastix, estimates a transformation :math:`T:` fixed image :math:`\\rightarrow` moving image. Arguments --------- fixed_image : str Image source of the fixed image (typically the reference image). moving_image : str Image source of the moving image (typically the image to be registered). affine_parameter_file : str or None Elastix parameter file for the primary affine transformation. bspline_parameter_file : str or None Elastix parameter file for the secondary non-linear transformation. result_directory : str or None Elastic result directory. processes : int or None Number of threads to use. Returns ------- result_directory : str Path to elastix result directory. """ if processes is None: processes = mp.cpu_count() check_elastix_initialized(); # result directory if result_directory == None: result_directory = tempfile.gettempdir(); if not os.path.exists(result_directory): os.mkdir(result_directory); # run elastix if bspline_parameter_file is None: cmd = '%s -threads %d -m %s -f %s -p %s -out %s' % (elastix_binary, processes, moving_image, fixed_image, affine_parameter_file, result_directory); elif affine_parameter_file is None: cmd = '%s -threads %d -m %s -f %s -p %s -out %s' % (elastix_binary, processes, moving_image, fixed_image, bspline_parameter_file, result_directory); else: cmd = '%s -threads %d -m %s -f %s -p %s -p %s -out %s' % (elastix_binary, processes, moving_image, fixed_image, affine_parameter_file, bspline_parameter_file, result_directory); res = os.system(cmd); if res != 0: raise RuntimeError('align: failed executing: ' + cmd); return result_directory
[docs]def transform(source, sink = [], transform_parameter_file = None, transform_directory = None, result_directory = None): """Transform a raw data set to reference using the elastix alignment results. Arguments --------- source : str or array Image source to be transformed. sink : str, [] or None Image sink to save transformed image to. If [] return the default name of the data file generated by transformix. transform_parameter_file : str or None Parameter file for the primary transformation. If None, the file is determined from the transform_directory. transform_directory : str or None Result directory of elastix alignment. If None the transform_parameter_file has to be given. result_directory : str or None The directorty for the transformix results. Returns ------- transformed : array or st Array or file name of the transformed data. Note ---- If the map determined by elastix is :math:`T: \\mathrm{fixed} \\rightarrow \\mathrm{moving}`, transformix on data works as :math:`T^{-1}(\\mathrm{data})`. """ check_elastix_initialized(); # image source = io.as_source(source); if isinstance(source, io.tif.Source): imgname = source.location; delete_image = None; else: imgname = os.path.join(tempfile.gettempdir(), 'elastix_input.tif'); io.write(source, imgname); delete_image = imgname; # result directory delete_result_directory = None; if result_directory == None: resultdirname = os.path.join(tempfile.gettempdir(), 'elastix_output'); delete_result_directory = resultdirname; else: resultdirname = result_directory; if not os.path.exists(resultdirname): os.makedirs(resultdirname); # tranformation parameter transform_parameter_dir, transform_parameter_file = transform_directory_and_file(transform_parameter_file = transform_parameter_file, transform_directory = transform_directory); set_path_transform_files(transform_parameter_dir); #transformix -in inputImage.ext -out outputDirectory -tp TransformParameters.txx cmd = '%s -in %s -out %s -tp %s' % (transformix_binary, imgname, resultdirname, transform_parameter_file); res = os.system(cmd); if res != 0: raise RuntimeError('transform_data: failed executing: ' + cmd); # read data and clean up if delete_image is not None: os.remove(delete_image); if sink == []: return result_data_file(resultdirname); elif sink is None: resultfile = result_data_file(resultdirname); result = io.read(resultfile); elif isinstance(sink, str): resultfile = result_data_file(resultdirname); result = io.convert(resultfile, sink); else: raise RuntimeError('transform_data: sink not valid!'); if delete_result_directory is not None: shutil.rmtree(delete_result_directory); return result;
[docs]def deformation_field(sink = [], transform_parameter_file = None, transform_directory = None, result_directory = None): """Create the deformation field T(x) - x. Arguments --------- sink : str, [] or None Image sink to save the transformation field; if [] return the default name of the data file generated by transformix. transform_parameter_file : str or None Parameter file for the primary transformation, if None, the file is determined from the transform_directory. transform_directory : str or None Result directory of elastix alignment, if None the transform_parameter_file has to be given. result_directory : str or None The directorty for the transformix results. Returns ------- deformation_field : array or str Array or file name of the deformation field data. Note ---- The map determined by elastix is :math:`T \\mathrm{fixed} \\rightarrow \\mathrm{moving}`. """ check_elastix_initialized(); # result directory delete_result_directory = None; if result_directory == None: resultdirname = os.path.join(tempfile.gettempdir(), 'elastix_output'); delete_result_directory = resultdirname; else: resultdirname = result_directory; if not os.path.exists(resultdirname): os.makedirs(resultdirname); # setup transformation transform_parameter_dir, transform_parameter_file = transform_directory_and_file(transform_parameter_file = transform_parameter_file, transform_directory = transform_directory); set_path_transform_files(transform_parameter_dir); #transformix -in inputImage.ext -out outputDirectory -tp TransformParameters.txt cmd = '%s -def all -out %s -tp %s' % (transformix_binary, resultdirname, transform_parameter_file) res = os.system(cmd); if res != 0: raise RuntimeError('deformation_field: failed executing: ' + cmd); # read result and clean up if sink == []: return result_data_file(resultdirname); elif sink is None: resultfile = result_data_file(resultdirname); result = io.read(resultfile); elif isinstance(sink, str): resultfile = result_data_file(resultdirname); result = io.convert(resultfile, sink); else: raise RuntimeError('deformation_field: sink not valid!'); if delete_result_directory is not None: shutil.rmtree(delete_result_directory); return result;
[docs]def deformation_distance(deformation_field, sink = None, scale = None): """Compute the distance field from a deformation vector field. Arguments --------- deformation_field : str or array Source of the deformation field determined by :func:`deformation_field`. sink : str or None Image sink to save the deformation field to. scale : tuple or None Scale factor for each dimension, if None = (1,1,1). Returns ------- deformation_distannce : array or st Array or file name of the deformation distance data. """ deformation_field = io.read(deformation_field); df = np.square(deformation_field); if not scale is None: for i in range(3): df[:,:,:,i] = df[:,:,:,i] * (scale[i] * scale[i]); df = np.sqrt(np.sum(df, axis = 3)); return io.write(sink, df);
############################################################################### ### Point transformations ###############################################################################
[docs]def write_points(filename, points, indices = False, binary = True): """Write points as elastix/transformix point file Arguments --------- filename : str File name of the elastix point file. points : array or str Source of the points. indices : bool Write as pixel indices or physical coordiantes. Returns ------- filename: str File name of the elastix point file. """ points = io.read(points); if binary: with open(filename, 'wb') as pointfile: if indices: np.array(1, dtype = np.int64).tofile(pointfile) else: np.array(0, dtype = np.int64).tofile(pointfile) num_points = np.array(len(points), dtype = np.int64); num_points.tofile(pointfile); points = np.asarray(points, dtype = np.double); points.tofile(pointfile); pointfile.close(); else: with open(filename, 'w') as pointfile: if indices: pointfile.write('index\n') else: pointfile.write('point\n') pointfile.write(str(points.shape[0]) + '\n'); np.savetxt(pointfile, points, delimiter = ' ', newline = '\n', fmt = '%.5e') pointfile.close(); return filename;
[docs]def read_points(filename, indices = False, binary = True): """Parses the output points from the output file of transformix Arguments --------- filename : str File name of the transformix output file. indices : bool If True return pixel indices otherwise float coordinates. Returns -------- points : array The point coordinates. """ if binary: with open(filename) as f: index = np.fromfile(f, dtype=np.int64, count = 1)[0]; #print(index) if index == 0: indices = False; else: indices = True; num_points = np.fromfile(f, dtype=np.int64, count = 1)[0]; #print(num_points) if num_points == 0: return np.zeros((0,3)); points = np.fromfile(f, dtype = np.double); #print points.shape points = np.reshape(points, (num_points,3)); f.close(); return points; else: # text file with open(filename) as f: lines = f.readlines() f.close(); num_points = len(lines); if num_points == 0: return np.zeros((0,3)); points = np.zeros((num_points, 3)); k = 0; for line in lines: ls = line.split(); if indices: for i in range(0,3): points[k,i] = float(ls[i+22]); else: for i in range(0,3): points[k,i] = float(ls[i+30]); k += 1; return points;
[docs]def transform_points(source, sink = None, transform_parameter_file = None, transform_directory = None, indices = False, result_directory = None, temp_file = None, binary = True): """Transform coordinates math:`x` via elastix estimated transformation to :math:`T(x)`. Arguments --------- source : str Source of the points. sink : str or None Sink for transformed points. transform_parameter_file : str or None Parameter file for the primary transformation. If None, the file is determined from the transform_directory. transform_directory : str or None Result directory of elastix alignment. If None the transform_parameter_file has to be given. indices : bool If True use points as pixel coordinates otherwise spatial coordinates. result_directory : str or None Elastic result directory. temp_file : str or None Optional file name for the elastix point file. Returns ------- points : array or st Array or file name of transformed points. Note ---- The transformation is from the fixed image coorindates to the moving image coordiantes. """ check_elastix_initialized(); # input point file if temp_file == None: if binary: temp_file = os.path.join(tempfile.gettempdir(), 'elastix_input.bin'); else: temp_file = os.path.join(tempfile.gettempdir(), 'elastix_input.txt'); delete_point_file = None; if isinstance(source, str): if len(source) > 3 and source[-3:] in ['txt', 'bin']: if source[-3:] == 'txt': binary = False; if source[-3] == 'bin': binary = True; pointfile = source; else: points = io.read(source); pointfile = temp_file; delete_point_file = temp_file; write_points(pointfile, points, indices = indices, binary = binary); elif isinstance(source, np.ndarray): pointfile = temp_file; delete_point_file = temp_file; write_points(pointfile, source, indices = indices, binary = binary); else: raise RuntimeError('transform_points: source not string or array!'); #print(pointfile) # result directory if result_directory == None: outdirname = os.path.join(tempfile.gettempdir(), 'elastix_output'); delete_result_directory = outdirname; else: outdirname = result_directory; delete_result_directory = None; if not os.path.exists(outdirname): os.makedirs(outdirname); #transform transform_parameter_dir, transform_parameter_file = transform_directory_and_file(transform_parameter_file = transform_parameter_file, transform_directory = transform_directory); set_path_transform_files(transform_parameter_dir); #run transformix cmd = '%s -def %s -out %s -tp %s' % (transformix_binary, pointfile, outdirname, transform_parameter_file); print(cmd) res = os.system(cmd); if res != 0: raise RuntimeError('failed executing ' + cmd); # read data and clean up if delete_point_file is not None: os.remove(delete_point_file); #read data / file if sink == []: # return sink as file name if binary: return os.path.join(outdirname, 'outputpoints.bin') else: return os.path.join(outdirname, 'outputpoints.txt') else: if binary: transpoints = read_points(os.path.join(outdirname, 'outputpoints.bin'), indices = indices, binary = True); else: transpoints = read_points(os.path.join(outdirname, 'outputpoints.txt'), indices = indices, binary = False); if delete_result_directory is not None: shutil.rmtree(delete_result_directory); return io.write(sink, transpoints);
[docs]def inverse_transform(fixed_image, affine_parameter_file, bspline_parameter_file = None, transform_parameter_file = None, transform_directory = None, result_directory = None, processes = None): """Estimate inverse tranformation :math:`T^{-1}:` moving image :math:`\\rightarrow` fixed image. Arguments --------- fixed_image : str Image source of the fixed image (typically the reference image) affine_parameter_file : str The paramter file for the original affine transformation. bspline_parameter_file : str The paramter file for the original b-spline transformation. transform_directory : str Elastic result directory of the original transform. result_directory : str or None Elastic result directory of the inverse transform. Returns ------- result_directory : str Path to elastix result directory. """ check_elastix_initialized(); # result directory if result_directory == None: result_directory = tempfile.getgettempdir()(); if not os.path.exists(result_directory): os.mkdir(result_directory); # transformation files transform_parameter_dir, transform_parameter_file = transform_directory_and_file(transform_parameter_file = transform_parameter_file, transform_directory = transform_directory); set_path_transform_files(transform_parameter_dir); #set metric of the parameter files if bspline_parameter_file is not None: _, bsplinefile = os.path.split(bspline_parameter_file); bsplinefile = os.path.join(result_directory, bsplinefile); shutil.copyfile(bspline_parameter_file, bsplinefile); set_metric_parameter_file(bsplinefile, 'DisplacementMagnitudePenalty'); else: bsplinefile = None; if affine_parameter_file is not None: _, affinefile = os.path.split(affine_parameter_file); affinefile = os.path.join(result_directory, affinefile); shutil.copyfile(affine_parameter_file, affinefile); set_metric_parameter_file(affinefile, 'DisplacementMagnitudePenalty'); else: affinefile = None; # run elastix if bsplinefile is None: cmd = '%s -threads %d -m %s -f %s -t0 %s -p %s -out %s' % (elastix_binary, processes, fixed_image, fixed_image, transform_parameter_file, affinefile, result_directory); elif affinefile is None: cmd = '%s -threads %d -m %s -f %s -t0 %s -p %s -out %s' % (elastix_binary, processes, fixed_image, fixed_image, transform_parameter_file, bsplinefile, result_directory); else: cmd = '%s -threads %d -m %s -f %s -t0 %s -p %s -p %s -out %s' % (elastix_binary, processes, fixed_image, fixed_image, transform_parameter_file, affinefile, bsplinefile, result_directory); res = os.system(cmd); if res != 0: raise RuntimeError('inverse_transform: failed executing: ' + cmd); return result_directory
############################################################################### ### Tests ############################################################################### def _test(): import os import numpy as np import ClearMap.Settings as settings import ClearMap.Alignment.Elastix as elx #reload(elx) path = os.path.join(settings.test_data_path, 'Elastix'); result_directory = os.path.join(path, 'elastix_template_to_ref') elx.transform_file(result_directory) shape = np.array([432, 512, 229]) points = np.random.rand(30,3) * 0.25 * shape + 0.5 * shape; points = np.round(points); points = np.array(points, dtype = int) test_txt = elx.transform_points(points, transform_directory=result_directory, binary=False, indices=False); test = elx.transform_points(points, transform_directory=result_directory, binary=True, indices=False); print(np.allclose(test_txt, test))