def __oddEvenNumberOfSlices(self, *args): """return a list of images that will count a odd number of slices in z direction If an even number of slices is found, the upper volume will be remove Args: *args: a list of images Returns: a list of images stripped """ output = [] for image in args: if image: try: zDims = int(mriutil.getMriDimensions(image)[2]) if zDims % 2 == 1: target = self.buildName(image, "subset") mriutil.extractSubVolume(image, target, '+2', "0:{}".format(zDims - 2), self.getNTreadsMrtrix()) output.append(target) else: output.append(image) except ValueError: output.append(image) else: output.append(False) return output
def __oddEvenNumberOfSlices(self, *args): """return a list of images that will count a odd number of slices in z direction If an even number of slices is found, the upper volume will be remove Args: *args: a list of images Returns: a list of images stripped """ output = [] for image in args: if image: try: zDims = int(mriutil.getMriDimensions(image)[2]) if zDims%2 == 1: target = self.buildName(image, "subset") mriutil.extractSubVolume(image, target, '+2',"0:{}".format(zDims-2), self.getNTreadsMrtrix()) output.append(target) else: output.append(image) except ValueError: output.append(image) else: output.append(False) return output
def __validateSizeAndDimension(self, *args): names = [] dims = [] sizes = [] for source in args: if source: names.append(source) dimensions = mriutil.getMriDimensions(source) if len(dimensions) == 4: dims.append([dimensions[0], dimensions[1], dimensions[2]]) else: dims.append(dimensions) # sourceSize sanitization sourceSize = mriutil.getMriVoxelSize(source) try: sourceSize = [ "{:.1f}".format(float(val)) for val in sourceSize ] except ValueError: self.error("Error during sourceSize sanitization") sizes.append(sourceSize) if not dims[1:] == dims[:-1]: self.error( "Dimension for each scale mismatch found between images: {}". format(", ".join(names))) if not sizes[1:] == sizes[:-1]: self.error("Voxel size mismatch found between images: {}".format( ", ".join(names)))
def __validateSizeAndDimension(self, *args): names = [] dims = [] sizes = [] for source in args: if source: names.append(source) dimensions = mriutil.getMriDimensions(source) if len(dimensions) == 4: dims.append([dimensions[0], dimensions[1], dimensions[2]]) else: dims.append(dimensions) # sourceSize sanitization sourceSize = mriutil.getMriVoxelSize(source) try: sourceSize = ["{:.1f}".format(float(val)) for val in sourceSize] except ValueError: self.error("Error during sourceSize sanitization") sizes.append(sourceSize) if not dims[1:] == dims[:-1]: self.error("Dimension for each scale mismatch found between images: {}".format(", ".join(names))) if not sizes[1:] == sizes[:-1]: self.error("Voxel size mismatch found between images: {}".format(", ".join(names)))
def implement(self): dwi = self.getImage(self.dependDir, 'dwi') b0PA = self.getImage(self.dependDir, 'b0PA') b0AP = self.getImage(self.dependDir, 'b0AP') bFile = self.getImage(self.dependDir, 'grad', None, 'b') bVals = self.getImage(self.dependDir, 'grad', None, 'bval') bVecs = self.getImage(self.dependDir, 'grad', None, 'bvec') #make sure the 3 images have the same voxel size and dimension scale self.__validateSizeAndDimension(dwi, b0PA, b0AP) #make sure that the z dimension contain an odd number of slices dwiZDims = int(mriutil.getMriDimensions(dwi)[2]) if dwiZDims%2 == 1: dwi = self.__extractZVolumes(dwi, "0:{}".format(dwiZDims-2)) b0PA = self.__extractZVolumes(b0PA, "0:{}".format(dwiZDims-2)) b0AP = self.__extractZVolumes(b0AP, "0:{}".format(dwiZDims-2)) #concatenate B0 image together b0Image = self.__concatenateB0(b0PA, b0AP, os.path.join(self.workingDir, self.get('b0s_filename'))) #create an empty b02b0.cnf file #b02b0File = os.path.join(self.workingDir, self.get('b02b0_filename')) #open(b02b0File, 'a').close() #create the acquisition parameter file acqpTopup = self.__createAcquisitionParameterFile('topup') acqpEddy = self.__createAcquisitionParameterFile('eddy') #create an index file indexFile = self.__createIndexFile(mriutil.getNbDirectionsFromDWI(dwi)) #Lauch topup on concatenate B0 image [topupBaseName, topupImage] = self.__topup(b0Image, acqpTopup, self.get('b02b0_filename')) outputFieldMask = self.__fslmaths_tmean(os.path.join(self.workingDir, topupImage)) outputFieldMaskExtracted = self.__bet(outputFieldMask) outputEddyImage = self.__correction_eddy2(dwi, outputFieldMaskExtracted, topupBaseName, indexFile, acqpEddy, bVecs, bVals) self.info("Uncompressing eddy output image: {}".format(outputEddyImage)) util.gunzip(outputEddyImage) #@TODO remove the glob and use getimage eddyParameterFiles = glob.glob("{}/*.eddy_parameters".format(self.workingDir)) if len(eddyParameterFiles)>0: bCorrected = mriutil.applyGradientCorrection(bFile, eddyParameterFiles.pop(0), self.workingDir) #produce the bVal and bVec file accordingly mriutil.bEnc2BVec(bCorrected, self.workingDir) mriutil.bEnc2BVal(bCorrected, self.workingDir)
def __validateSizeAndDimension(self, dwi, b0PA, b0AP): dwiDim = mriutil.getMriDimensions(dwi) dwiVoxel = mriutil.getMriVoxelSize(dwi) b0PADim = mriutil.getMriDimensions(b0PA) b0PAVoxel = mriutil.getMriVoxelSize(b0PA) b0APDim = mriutil.getMriDimensions(b0AP) b0APVoxel = mriutil.getMriVoxelSize(b0AP) self.info("Look if {} and {} and {} have the same voxel size".format(dwi, b0PA, b0AP)) if len(dwiVoxel) == len(b0PAVoxel) == len(b0APVoxel) == 3: for i in range(0,len(dwiVoxel)): if not (dwiVoxel[i] == b0PAVoxel[i] == b0APVoxel[i]): self.error("Voxel size mismatch found at index {} for image {} {} {}".format(i, dwi, b0PA, b0AP)) else: self.error("Found Voxel size inconsistency for image {} or {} or {}".format(dwi, b0PA, b0AP)) self.info("Look if {} and {} and {} have the same dimension for each scale".format(dwi, b0PA, b0AP)) for i in range(0,3): if not (dwiDim[i]==b0PADim[i]==b0APDim[i]): self.error("Dimensions mismatch found at index {} for image {} {} {}".format(i, dwi, b0PA, b0AP))
def qaSupplier(self): """Create and supply images for the report generated by qa task """ qaImages = Images() tags = ( ('anat', self.plot3dVolume, 'High resolution anatomical image'), ('dwi', self.plot4dVolume, 'Diffusion weighted image')) tags = list(tags) if self.getImage('b0_pa'): if len(mriutil.getMriDimensions(self.getImage('b0_pa')))==4: tags.append(('b0_pa', self.plot4dVolume, 'B0 PA images')) else: tags.append(('b0_pa', self.plot3dVolume, 'B0 PA image')) if self.getImage('b0_ap'): if len(mriutil.getMriDimensions(self.getImage('b0_ap')))==4: tags.append(('b0_ap', self.plot4dVolume, 'B0 AP images')) else: tags.append(('b0_ap', self.plot3dVolume, 'B0 AP image')) tags.append(('mag', self.plot3dVolume, 'Magnitude image')) tags.append(('phase', self.plot3dVolume, 'Phase image')) tags = tuple(tags) for prefix, plotMethod, description in tags: source = self.getImage(prefix) if source: qaImage = plotMethod(source) qaImages.append((qaImage, description)) return qaImages
def __checkOddEvenNumberOfSlices(self, source): """return the config file b02b0 If an even number of slices is found, b02b0_even will be used otherwise we'll b02b0_odd Args: *args: a list of images Returns: config file b02b0 """ zDims = int(mriutil.getMriDimensions(source)[2]) if zDims % 2 == 1: return self.get('b02b0_odd') else: return self.get('b02b0_even')
def __validateSizeAndDimension(self, *args): names = [] dims = [] sizes = [] for source in args: if source: names.append(source) dimensions = mriutil.getMriDimensions(source) if len(dimensions) == 4: dims.append([dimensions[0], dimensions[1], dimensions[2]]) else: dims.append(dimensions) sizes.append(mriutil.getMriVoxelSize(source)) if not dims[1:] == dims[:-1]: self.error("Dimension for each scale mismatch found between images: {}".format(", ".join(names))) if not sizes[1:] == sizes[:-1]: self.error("Voxel size mismatch found between images: {}".format(", ".join(names)))
def __correctionEddy(self, source, mask, topup, index, acqp, bVecs, bVals, bEnc): """Performs eddy correction on a dwi file. Args: source: File containing all the images to estimate distortions for mask: Mask to indicate brain topup: Base name for output files from topup index: File containing indices for all volumes in --imain into --acqp and --topup acqp: File containing acquisition parameters bvecs: File containing the b-vectors for all volumes in --imain bvals: File containing the b-values for all volumes in --imain Returns: The resulting file name """ self.info("Launch eddy correction from fsl") tmp = self.buildName(source, "tmp") target = self.buildName(source, "eddy") cmd = "eddy_openmp_patch --imain={} --mask={} --index={} --acqp={} --bvecs={} --bvals={} --out={} " \ .format(source, mask, index, acqp, bVecs, bVals, tmp) if len(mriutil.getBValues(source, bEnc))>2: #cmd += " --data_is_shelled --mb={} " \ cmd += " --data_is_shelled" zDims = int(mriutil.getMriDimensions(source)[2]) if zDims % int(self.get('multiband')) == 0: cmd += " --mb={}".format(self.get('multiband')) # .format(self.get('multiband')) if topup is not None: cmd += " --topup={}".format(topup) self.getNTreadsEddy() self.launchCommand(cmd, nice=5*60*60) return self.rename(tmp, target)
def implement(self): dwi = self.getDenoisingImage('dwi', 'denoise') if not dwi: dwi = self.getPreparationImage('dwi') b0AP = self.getPreparationImage('b0_ap') b0PA = self.getPreparationImage('b0_pa') bEnc = self.getPreparationImage('grad', None, 'b') bVals = self.getPreparationImage('grad', None, 'bvals') bVecs = self.getPreparationImage('grad', None, 'bvecs') norm = self.getParcellationImage('norm') parcellationMask = self.getParcellationImage('mask') # Fieldmap only mag = self.getPreparationImage("mag") phase = self.getPreparationImage("phase") freesurferAnat = self.getParcellationImage('anat', 'freesurfer') self.info("extract b0 image from the dwi") b0 = os.path.join(self.workingDir, os.path.basename(dwi).replace(self.get("prefix", 'dwi'), self.get("prefix", 'b0'))) # Extract first n b0s from DWI self.info(mriutil.extractFirstB0sFromDWI(dwi, b0, bVals, self.getNTreadsMrtrix())) # Get number of b0s extracted from DWI tDims = int(mriutil.getMriDimensions(b0)[3]) self.info("look if all images have the same voxel size and dimension scale") self.__validateSizeAndDimension(dwi, b0, b0AP, b0PA) # Generate a missing b0 image if we could. --> 0 = P>>A, 1 = A>>P if self.get("phase_enc_dir") == "0" and b0AP and b0PA is False: b0PA = b0 # Extract same number of volumes b0PA try: b0APDim = int(mriutil.getMriDimensions(b0AP)[3]) target = os.path.join(self.workingDir, os.path.basename(dwi).replace(self.get("prefix", 'dwi'), self.get("prefix", 'b0_ap'))) self.info(mriutil.extractSubVolume(b0AP, target, '+3', "{}:{}".format(b0APDim-tDims, b0APDim-1 ), self.getNTreadsMrtrix())) b0AP = target except: b0APDim = 1 if self.get("phase_enc_dir") == "1" and b0PA and b0AP is False: b0AP = b0 # Extract same number of volumes b0PA try: b0PADim = int(mriutil.getMriDimensions(b0PA)[3]) target = os.path.join(self.workingDir, os.path.basename(dwi).replace(self.get("prefix", 'dwi'), self.get("prefix", 'b0_pa'))) self.info(mriutil.extractSubVolume(b0PA, target, '+3', "{}:{}".format(b0PADim-tDims, b0PADim-1), self.getNTreadsMrtrix())) b0PA = target except: b0PADim = 1 topupConfigFile = self.__checkOddEvenNumberOfSlices(dwi) self.set('method', None) if b0AP is False or b0PA is False: topupBaseName = None b0Image = b0 self.set('method', 'fieldmap') else: # Concatenate B0 image together if self.get("phase_enc_dir") == "0": concatenateB0Image = self.__concatenateB0(b0PA, b0AP, self.buildName("b0pa_b0ap", None, "nii.gz")) self.dimB0s = [mriutil.getNbDirectionsFromDWI(b0PA), mriutil.getNbDirectionsFromDWI(b0AP)] elif self.get("phase_enc_dir") == "1": concatenateB0Image = self.__concatenateB0(b0AP, b0PA, self.buildName("b0ap_b0pa", None, "nii.gz")) self.dimB0s = [mriutil.getNbDirectionsFromDWI(b0AP), mriutil.getNbDirectionsFromDWI(b0PA)] if int(mriutil.getMriDimensions(dwi)[2]) % 2 == 1: # Number of slice in z axis if 'force' not in self.get('crop'): # Use odd number [dwi, b0, b0AP, b0PA, concatenateB0Image] = self.__cropNumberOfSlices(dwi, b0, b0AP, b0PA, concatenateB0Image) # Select config File for topup topupConfigFile = self.__checkOddEvenNumberOfSlices(dwi) # Create the acquisition parameter file acqpTopup = self.__createAcquisitionParameterFile('topup') # Run topup on concatenate B0 image [topupBaseName, topupImage] = self.__topup(concatenateB0Image, acqpTopup, topupConfigFile) b0Image = self.__fslmathsTmean(os.path.join(self.workingDir, topupImage)) self.set('method', 'topup') self.info("create a suitable mask for the dwi") extraArgs = " -dof 6 " # same subject if self.get("methodology", "intrasession"): extraArgs += " -usesqform " mask, _notUsed_a, _notUsed_b = mriutil.computeDwiMaskFromFreesurfer(b0Image, norm, parcellationMask, self.buildName(parcellationMask, 'temporary'), extraArgs) # Create the acquisition parameter file for eddy acqpEddy = self.__createAcquisitionParameterFile('eddy') # Create an index file indexFile = self.__createIndexFile(mriutil.getNbDirectionsFromDWI(dwi)) outputImage = self.__correctionEddy(dwi, mask, topupBaseName, indexFile, acqpEddy, bVecs, bVals, bEnc) eddyParameterFiles = self.getImage('dwi', None, 'eddy_parameters') if eddyParameterFiles: self.info("Apply eddy movement correction to gradient encodings directions") bEnc = mriutil.applyGradientCorrection(bEnc, eddyParameterFiles, self.buildName(outputImage, None, 'b')) self.info(mriutil.mrtrixToFslEncoding(outputImage, bEnc, self.buildName(outputImage, None, 'bvecs'), self.buildName(outputImage, None, 'bvals'))) # Proceed with fieldmap if provided if mag and phase and not self.__topupCorrection: # OutputImage is now used for fieldmap correction outputImage = self.__computeFieldmap(outputImage, bVals, mag, phase, norm, parcellationMask, freesurferAnat) self.set('method', 'fieldmap') # Produce a valid b0 and mask for QA b0Corrected = self.buildName(b0, 'corrected') self.info(mriutil.extractFirstB0FromDwi(outputImage, b0Corrected, bVals)) maskCorrected = mriutil.computeDwiMaskFromFreesurfer(b0Corrected, norm, parcellationMask, self.buildName(parcellationMask, 'corrected'), extraArgs) self.rename(outputImage, self.buildName(outputImage, 'corrected'))