Source code for hdireg.HDIreg.elastix

#Image registration using elastix
#Joshua Hess

#Import modules
import numpy as np
import sys
import time
import os
import re
import nibabel as nib
from pathlib import Path
import pandas as pd
import tempfile


#Add main elastix component
[docs]def RunElastix(command): """ Run the elastix registration. You must be able to call elastix from your command shell to use this. You must also have your parameter text files set before running (see elastix parameter files). Parameters ---------- command: string Sent to the system for elastix running (see elastix command line implementation). """ #Print command print(str(command)) #Print elastix update print('Running elastix...') #Start timer start = time.time() #Send the command to the shell os.system(command) #Stop timer stop = time.time() #Print update print('Finished -- computation took '+str(stop-start)+'sec.') #Return values return command
#Define elastix class structure
[docs]class Elastix(): """Elastix image registration class. Parameters ---------- fixed: string Path to fixed (reference) image. moving: string Path to moving image (image to be transformed). out_dir: string Path to output directory. p: list (length number of registration parameter files) Path to elastix image registration parameter files (in order of application). fp: string (*.txt) Path to fixed image landmark points for manual guidance registration. mp: string (*.txt) Path to moving image landmark points for manual guidance registration. fMask: string (*.nii) Path to fixed image mask that defines region on image to draw samples from during registration. """ def __init__(self,fixed,moving,out_dir,p,fp=None,mp=None,fMask=None): #Create pathlib objects and set class parameters self.fixed = Path(fixed) self.fixed_channels = [] self.moving_channels = [] self.multichannel = None self.moving = Path(moving) self.out_dir = Path(out_dir) self.temp_dir = None self.p = [Path(par_file) for par_file in p] self.fp = None if fp is None else Path(fp) self.mp = None if mp is None else Path(fp) self.fMask = None if fMask is None else Path(fMask) self.command = "elastix" #Load the images to check for dimension number print('Loading images...') #Load images niiFixed = nib.load(str(self.fixed)) niiMoving = nib.load(str(self.moving)) #Print update print('Done loading') #Add the parameter files self.command = self.command+' '.join([" -p "+str(self.p[par_file]) for par_file in range(len(self.p))]) #Check for corresponding points in registration (must have fixed and moving set both) if self.fp and self.mp is not None: #Add to the command self.command = self.command +" -fp "+str(self.fp)+" -mp "+str(self.mp) #Check for fixed mask if fMask is not None: #Add the fixed mask to the command if it exists self.command = self.command +" -fMask "+str(fMask) #Add the output directory to the command self.command = self.command +" -out "+str(self.out_dir) #Check to see if there is single channel input (grayscale) if niiFixed.ndim == 2 and niiMoving.ndim == 2: print('Detected single channel input images...') #Add fixed and moving image to the command string self.command = self.command+" -f "+str(self.fixed)+ " -m "+str(self.moving) #Update the fixed channels self.fixed_channels.append(self.fixed) #Update the moving channels self.moving_channels.append(self.moving) #Update whether this is a multichannel input or not self.multichannel = False #Run elastix without creating temporary directory RunElastix(self.command) #Check to see if there is multichannel input else: #create a temporary directory using the context manager for channel-wise images with tempfile.TemporaryDirectory(dir=self.out_dir) as tmpdirname: #Print update print('Created temporary directory', tmpdirname) #Print update print('Exporting single channel images for multichannel input...') #Read the images niiFixed = niiFixed.get_fdata() niiMoving = niiMoving.get_fdata() #Update multichannel class option self.multichannel = True #Export single channel images for each channel of fixed image for i in range(niiFixed.shape[2]): #Create a filename fname = Path(os.path.join(tmpdirname,str(self.fixed.stem+str(i)+self.fixed.suffix))) #Update the list of names for fixed image self.fixed_channels.append(fname) #Update the list of names for fixed image self.command = self.command + ' -f' + str(i) + ' ' + str(fname) #Check to see if the path exists if not fname.is_file(): #Create a nifti image nii_im = nib.Nifti1Image(niiFixed[:,:,i], affine=np.eye(4)) #Save the nifti image nib.save(nii_im,str(fname)) #Remove the fixed image from memory niiFixed = None #Export single channel images for each channel of fixed image for i in range(niiMoving.shape[2]): #Create a filename mname = Path(os.path.join(tmpdirname,str(self.moving.stem+str(i)+self.moving.suffix))) #Update the list of names for moving image self.moving_channels.append(mname) #Update the list of names for moving image self.command = self.command + ' -m' + str(i) + ' ' + str(mname) #Check to see if the path exists if not mname.is_file(): #Create a nifti image nii_im = nib.Nifti1Image(niiMoving[:,:,i], affine=np.eye(4)) #Save the nifti image nib.save(nii_im,str(mname)) #Remove the moving image from memory niiMoving = None #Run the command using the function created RunElastix(self.command)
#Return the command itself #return self.command