def testEstimateMonomodalDiffeomorphicField3DMultiScale(lambdaParam): fnameMoving = 'data/affineRegistered/templateT1ToIBSR01T1.nii.gz' fnameFixed = 'data/t1/IBSR18/IBSR_01/IBSR_01_ana_strip.nii.gz' moving = nib.load(fnameMoving) fixed = nib.load(fnameFixed) moving = moving.get_data().squeeze().astype(np.float64) fixed = fixed.get_data().squeeze().astype(np.float64) moving = np.copy(moving, order='C') fixed = np.copy(fixed, order='C') moving = (moving - moving.min()) / (moving.max() - moving.min()) fixed = (fixed - fixed.min()) / (fixed.max() - fixed.min()) level = 3 #maskMoving=np.ones_like(moving) #maskFixed=np.ones_like(fixed) movingPyramid = [ img for img in rcommon.pyramid_gaussian_3D(moving, level, np.ones_like(moving)) ] fixedPyramid = [ img for img in rcommon.pyramid_gaussian_3D(fixed, level, np.ones_like(fixed)) ] rcommon.plotOverlaidPyramids3DCoronal(movingPyramid, fixedPyramid) #maxOuterIter=[100,100,100,100,100,100,100,100,100] maxOuterIter = [3, 3, 3, 3, 3, 3, 3, 3, 3] #maxOuterIter=[10,20,50,100, 100, 100] displacement = estimateMonomodalDiffeomorphicField3DMultiScale( movingPyramid, fixedPyramid, lambdaParam, maxOuterIter, 0, None) warped = tf.warp_volume(movingPyramid[0], displacement) np.save('displacement_templateT1ToIBSR01T1_diff.npy', displacement) np.save('warped_templateT1ToIBSR01T1_diff.npy', warped)
def warp_backward(self, image): r''' Applies this transformation in the backward direction to the given image using tri-linear interpolation ''' if len(image.shape) == 3: if image.dtype is np.dtype('int32'): warped = np.array( tf.warp_discrete_volumeNN( image, self.backward, self.affine_post_inv, self.affine_pre_inv)) elif image.dtype is np.dtype('float64'): warped = np.array( tf.warp_volume( image, self.backward, self.affine_post_inv, self.affine_pre_inv)) else: if image.dtype is np.dtype('int32'): warped = np.array( tf.warp_discrete_imageNN( image, self.backward, self.affine_post_inv, self.affine_pre_inv)) elif image.dtype is np.dtype('float64'): warped = np.array( tf.warp_image( image, self.backward, self.affine_post_inv, self.affine_pre_inv)) return warped
def saveDeformedLattice3D(displacement, oname): minVal, maxVal=tf.get_displacement_range(displacement, None) sh=np.array([np.ceil(maxVal[0]),np.ceil(maxVal[1]),np.ceil(maxVal[2])], dtype=np.int32) L=np.array(rcommon.drawLattice3D(sh, 10)) warped=np.array(tf.warp_volume(L, displacement, np.eye(4))).astype(np.int16) img=nib.Nifti1Image(warped, np.eye(4)) img.to_filename(oname)
def saveDeformedLattice3D(displacement, oname): minVal, maxVal = tf.get_displacement_range(displacement, None) sh = np.array([np.ceil(maxVal[0]), np.ceil(maxVal[1]), np.ceil(maxVal[2])], dtype=np.int32) L = np.array(rcommon.drawLattice3D(sh, 10)) warped = np.array(tf.warp_volume(L, displacement, np.eye(4))).astype(np.int16) img = nib.Nifti1Image(warped, np.eye(4)) img.to_filename(oname)
def save_deformed_lattice_3d(displacement, oname): r''' Applies the given displacement to a regular lattice and saves the resulting image to a Nifti file with the given name ''' min_val, max_val = tf.get_displacement_range(displacement, None) shape = np.array([np.ceil(max_val[0]), np.ceil(max_val[1]), np.ceil(max_val[2])], dtype = np.int32) lattice = np.array(rcommon.drawLattice3D(shape, 10)) warped = np.array(tf.warp_volume(lattice, displacement)).astype(np.int16) img = nib.Nifti1Image(warped, np.eye(4)) img.to_filename(oname)
def estimateNewMonomodalDiffeomorphicField3D(moving, fixed, lambdaParam, maxOuterIter, previousDisplacement, reportProgress=False): ''' Warning: in the monomodal case, the parameter lambda must be significantly lower than in the multimodal case. Try lambdaParam=1, as opposed as lambdaParam=150 used in the multimodal case ''' innerTolerance = 1e-3 outerTolerance = 1e-3 displacement = np.zeros(shape=(moving.shape) + (3, ), dtype=np.float64) residuals = np.zeros(shape=(moving.shape), dtype=np.float64) gradientField = np.empty(shape=(moving.shape) + (3, ), dtype=np.float64) totalDisplacement = np.zeros(shape=(moving.shape) + (3, ), dtype=np.float64) if (previousDisplacement != None): totalDisplacement[...] = previousDisplacement outerIter = 0 while (outerIter < maxOuterIter): outerIter += 1 if (reportProgress): print 'Iter:', outerIter, '/', maxOuterIter warped = np.array(tf.warp_volume(moving, totalDisplacement)) sigmaField = np.ones_like(warped, dtype=np.float64) deltaField = fixed - warped g0, g1, g2 = sp.gradient(warped) gradientField[:, :, :, 0] = g0 gradientField[:, :, :, 1] = g1 gradientField[:, :, :, 2] = g2 maxVariation = 1 + innerTolerance innerIter = 0 maxResidual = 0 displacement[...] = 0 maxInnerIter = 50 while ((maxVariation > innerTolerance) and (innerIter < maxInnerIter)): innerIter += 1 maxVariation = tf.iterateDisplacementField3DCYTHON( deltaField, sigmaField, gradientField, lambdaParam, totalDisplacement, displacement, residuals) opt = np.max(residuals) if (maxResidual < opt): maxResidual = opt maxDisplacement = np.max(np.abs(displacement)) totalDisplacement, stats = tf.compose_vector_fields3D( displacement, totalDisplacement) if (maxDisplacement < outerTolerance): break print "Iter: ", outerIter, "Max lateral displacement:", maxDisplacement, "Max variation:", maxVariation, "Max residual:", maxResidual if (previousDisplacement != None): return totalDisplacement - previousDisplacement return totalDisplacement
def save_registration_results(init_affine, displacement, inverse, params): r''' Warp the target image using the obtained deformation field ''' fixed = nib.load(params.reference) fixed_affine = fixed.get_affine() reference_shape = np.array(fixed.shape, dtype=np.int32) warp_dir = params.warp_dir base_moving = rcommon.getBaseFileName(params.target) base_fixed = rcommon.getBaseFileName(params.reference) moving = nib.load(params.target).get_data().squeeze().astype(np.float64) moving = moving.copy(order='C') warped = np.array(tf.warp_volume(moving, displacement)).astype(np.int16) img_warped = nib.Nifti1Image(warped, fixed_affine) img_warped.to_filename('warpedDiff_'+base_moving+'_'+base_fixed+'.nii.gz') #---warp the target image using the affine transformation only--- moving = nib.load(params.target).get_data().squeeze().astype(np.float64) moving = moving.copy(order='C') warped = np.array( tf.warp_volume_affine(moving, reference_shape, init_affine) ).astype(np.int16) img_warped = nib.Nifti1Image(warped, fixed_affine) img_warped.to_filename('warpedAffine_'+base_moving+'_'+base_fixed+'.nii.gz') #---warp all volumes in the warp directory using NN interpolation names = [os.path.join(warp_dir, name) for name in os.listdir(warp_dir)] for name in names: to_warp = nib.load(name).get_data().squeeze().astype(np.int32) to_warp = to_warp.copy(order='C') base_warp = rcommon.getBaseFileName(name) warped = np.array( tf.warp_discrete_volumeNN(to_warp, displacement)).astype(np.int16) img_warped = nib.Nifti1Image(warped, fixed_affine) img_warped.to_filename('warpedDiff_'+base_warp+'_'+base_fixed+'.nii.gz') #---finally, the optional output if params.output_list == None: return if 'lattice' in params.output_list: save_deformed_lattice_3d( displacement, 'latticeDispDiff_'+base_moving+'_'+base_fixed+'.nii.gz') if 'inv_lattice' in params.output_list: save_deformed_lattice_3d( inverse, 'invLatticeDispDiff_'+base_moving+'_'+base_fixed+'.nii.gz') if 'displacement' in params.output_list: np.save('dispDiff_'+base_moving+'_'+base_fixed+'.npy', displacement) if 'inverse' in params.output_list: np.save('invDispDiff_'+base_moving+'_'+base_fixed+'.npy', inverse)
def warpNonlinear(targetName, referenceName, dispName, oname, interpolationType='trilinear'): baseName=rcommon.getBaseFileName(targetName) displacement=np.load(dispName) nib_target = nib.load(targetName) if interpolationType=='NN': target=nib_target.get_data().squeeze().astype(np.int32) target=np.copy(target, order='C') warped=np.array(tf.warp_discrete_volumeNN(target, displacement)) else: target=nib_target.get_data().squeeze().astype(np.float64) target=np.copy(target, order='C') warped=np.array(tf.warp_volume(target, displacement)) referenceAffine=nib.load(referenceName).get_affine() warped=nib.Nifti1Image(warped, referenceAffine) if not oname: oname="warped"+baseName+"nii.gz" warped.to_filename(oname)
def estimateNewMonomodalDiffeomorphicField3D(moving, fixed, lambdaParam, maxOuterIter, previousDisplacement, reportProgress=False): ''' Warning: in the monomodal case, the parameter lambda must be significantly lower than in the multimodal case. Try lambdaParam=1, as opposed as lambdaParam=150 used in the multimodal case ''' innerTolerance=1e-3 outerTolerance=1e-3 displacement =np.zeros(shape=(moving.shape)+(3,), dtype=np.float64) residuals=np.zeros(shape=(moving.shape), dtype=np.float64) gradientField =np.empty(shape=(moving.shape)+(3,), dtype=np.float64) totalDisplacement=np.zeros(shape=(moving.shape)+(3,), dtype=np.float64) if(previousDisplacement!=None): totalDisplacement[...]=previousDisplacement outerIter=0 while(outerIter<maxOuterIter): outerIter+=1 if(reportProgress): print 'Iter:',outerIter,'/',maxOuterIter warped=np.array(tf.warp_volume(moving, totalDisplacement)) sigmaField=np.ones_like(warped, dtype=np.float64) deltaField=fixed-warped g0, g1, g2=sp.gradient(warped) gradientField[:,:,:,0]=g0 gradientField[:,:,:,1]=g1 gradientField[:,:,:,2]=g2 maxVariation=1+innerTolerance innerIter=0 maxResidual=0 displacement[...]=0 maxInnerIter=50 while((maxVariation>innerTolerance)and(innerIter<maxInnerIter)): innerIter+=1 maxVariation=tf.iterateDisplacementField3DCYTHON(deltaField, sigmaField, gradientField, lambdaParam, totalDisplacement, displacement, residuals) opt=np.max(residuals) if(maxResidual<opt): maxResidual=opt maxDisplacement=np.max(np.abs(displacement)) totalDisplacement, stats=tf.compose_vector_fields3D(displacement, totalDisplacement) if(maxDisplacement<outerTolerance): break print "Iter: ",outerIter, "Max lateral displacement:", maxDisplacement, "Max variation:",maxVariation, "Max residual:", maxResidual if(previousDisplacement!=None): return totalDisplacement-previousDisplacement return totalDisplacement
def saveDeformedLattice3D(dname, oname='deformed_lattice.nii.gz'): ''' saveDeformedLattice3D('displacement_templateT1ToIBSR01T1_diff.npy') saveDeformedLattice3D('displacement_templateT1ToIBSR01T1_diffMulti.npy') ''' print 'Loading displacement...' displacement=np.load(dname) minVal, maxVal=tf.get_displacement_range(displacement, None) sh=np.array([np.ceil(maxVal[0]),np.ceil(maxVal[1]),np.ceil(maxVal[2])], dtype=np.int32) print sh.dtype print sh L=np.array(drawLattice3D(sh, 10)) print 'Warping lattice...' warped=np.array(tf.warp_volume(L, displacement, np.eye(4))).astype(np.int16) print 'Transforming to Nifti...' img=nib.Nifti1Image(warped, np.eye(4)) print 'Saving warped lattice as:',oname img.to_filename(oname) print 'done.'
def testEstimateMonomodalDiffeomorphicField3DMultiScale(lambdaParam): fnameMoving='data/affineRegistered/templateT1ToIBSR01T1.nii.gz' fnameFixed='data/t1/IBSR18/IBSR_01/IBSR_01_ana_strip.nii.gz' moving = nib.load(fnameMoving) fixed= nib.load(fnameFixed) moving=moving.get_data().squeeze().astype(np.float64) fixed=fixed.get_data().squeeze().astype(np.float64) moving=np.copy(moving, order='C') fixed=np.copy(fixed, order='C') moving=(moving-moving.min())/(moving.max()-moving.min()) fixed=(fixed-fixed.min())/(fixed.max()-fixed.min()) level=3 #maskMoving=np.ones_like(moving) #maskFixed=np.ones_like(fixed) movingPyramid=[img for img in rcommon.pyramid_gaussian_3D(moving, level, np.ones_like(moving))] fixedPyramid=[img for img in rcommon.pyramid_gaussian_3D(fixed, level, np.ones_like(fixed))] rcommon.plotOverlaidPyramids3DCoronal(movingPyramid, fixedPyramid) #maxOuterIter=[100,100,100,100,100,100,100,100,100] maxOuterIter=[3,3,3,3,3,3,3,3,3] #maxOuterIter=[10,20,50,100, 100, 100] displacement=estimateMonomodalDiffeomorphicField3DMultiScale(movingPyramid, fixedPyramid, lambdaParam, maxOuterIter, 0,None) warped=tf.warp_volume(movingPyramid[0], displacement) np.save('displacement_templateT1ToIBSR01T1_diff.npy', displacement) np.save('warped_templateT1ToIBSR01T1_diff.npy', warped)
def estimateNewMultimodalDiffeomorphicField3D(moving, fixed, initAffine, lambdaDisplacement, quantizationLevels, maxOuterIter, previousDisplacement, reportProgress=False): innerTolerance = 1e-3 outerTolerance = 1e-3 displacement = np.empty(shape=(fixed.shape) + (3, ), dtype=np.float64) residuals = np.zeros(shape=(fixed.shape), dtype=np.float64) gradientField = np.empty(shape=(fixed.shape) + (3, ), dtype=np.float64) totalDisplacement = np.zeros(shape=(fixed.shape) + (3, ), dtype=np.float64) if (previousDisplacement != None): totalDisplacement[...] = previousDisplacement fixedQ = None grayLevels = None fixedQ, grayLevels, hist = tf.quantizePositiveVolumeCYTHON( fixed, quantizationLevels) fixedQ = np.array(fixedQ, dtype=np.int32) finished = False outerIter = 0 maxDisplacement = None maxVariation = None maxResidual = 0 fixedMask = (fixed > 0).astype(np.int32) movingMask = (moving > 0).astype(np.int32) trustRegion = fixedMask * np.array( tf.warp_discrete_volumeNNAffine( movingMask, np.array(fixedMask.shape, dtype=np.int32), initAffine)) #consider only the overlap after affine registration while ((not finished) and (outerIter < maxOuterIter)): outerIter += 1 if (reportProgress): print 'Iter:', outerIter, '/', maxOuterIter #sys.stdout.flush() #---E step--- #print "Warping..." #sys.stdout.flush() warped = np.array(tf.warp_volume(moving, totalDisplacement, initAffine)) warpedMask = np.array( tf.warp_discrete_volumeNN( trustRegion, totalDisplacement, np.eye(4))).astype( np.int32) #the affine mapping was already applied #print "Warping NN..." #sys.stdout.flush() #warpedMovingMask=np.array(tf.warp_volumeNN(movingMask, totalDisplacement)).astype(np.int32) #print "Class stats..." #sys.stdout.flush() means, variances = tf.computeMaskedVolumeClassStatsCYTHON( warpedMask, warped, quantizationLevels, fixedQ) means[0] = 0 means = np.array(means) variances = np.array(variances) sigmaField = variances[fixedQ] deltaField = means[ fixedQ] - warped #########Delta-field using Arce's rule #--M step-- g0, g1, g2 = sp.gradient(warped) gradientField[:, :, :, 0] = g0 gradientField[:, :, :, 1] = g1 gradientField[:, :, :, 2] = g2 maxVariation = 1 + innerTolerance innerIter = 0 maxInnerIter = 100 displacement[...] = 0 #print "Iterating..." #sys.stdout.flush() while ((maxVariation > innerTolerance) and (innerIter < maxInnerIter)): innerIter += 1 maxVariation = tf.iterateDisplacementField3DCYTHON( deltaField, sigmaField, gradientField, lambdaDisplacement, totalDisplacement, displacement, residuals) opt = np.max(residuals) if (maxResidual < opt): maxResidual = opt #--accumulate displacement-- #print "Exponential3D. Range D:", displacement.min(), displacement.max() #sys.stdout.flush() expd, inverseNone = tf.vector_field_exponential3D(displacement, False) expd = np.array(expd) #print "Range expd:", expd.min(), expd.max(), "Range TD:", totalDisplacement.min(), totalDisplacement.max() #print "Compose vector fields..." #sys.stdout.flush() totalDisplacement, stats = tf.compose_vector_fields3D( expd, totalDisplacement) totalDisplacement = np.array(totalDisplacement) #print "Composed rage:", totalDisplacement.min(), totalDisplacement.max() #sys.stdout.flush() #--check stop condition-- nrm = np.sqrt(displacement[..., 0]**2 + displacement[..., 1]**2 + displacement[..., 2]**2) #maxDisplacement=np.max(nrm) maxDisplacement = np.mean(nrm) if ((maxDisplacement < outerTolerance) or (outerIter >= maxOuterIter)): finished = True print "Iter: ", outerIter, "Mean displacement:", maxDisplacement, "Max variation:", maxVariation, "Max residual:", maxResidual #sh=fixed.shape #rcommon.overlayImages(warped[:,sh[1]//2,:], fixed[:,sh[1]//2,:]) #rcommon.overlayImages(warped[:,sh[1]//2,:]*warpedMask[:,sh[1]//2,:], fixed[:,sh[1]//2,:]) #sys.stdout.flush() if (previousDisplacement != None): #print 'Range TD:', totalDisplacement.min(), totalDisplacement.max(),'. Range PD:', previousDisplacement.min(), previousDisplacement.max() #sys.stdout.flush() return totalDisplacement - previousDisplacement return totalDisplacement
def testEstimateMultimodalSyN3DMultiScale(fnameMoving, fnameFixed, fnameAffine, warpDir, lambdaParam): ''' testEstimateMultimodalDiffeomorphicField3DMultiScale('IBSR_01_ana_strip.nii.gz', 't1_icbm_normal_1mm_pn0_rf0_peeled.nii.gz', 'IBSR_01_ana_strip_t1_icbm_normal_1mm_pn0_rf0_peeledAffine.txt', 100) ''' print 'Registering', fnameMoving, 'to', fnameFixed, 'with lambda=', lambdaParam sys.stdout.flush() moving = nib.load(fnameMoving) fixed = nib.load(fnameFixed) referenceShape = np.array(fixed.shape, dtype=np.int32) M = moving.get_affine() F = fixed.get_affine() if not fnameAffine: T = np.eye(4) else: T = rcommon.readAntsAffine(fnameAffine) initAffine = np.linalg.inv(M).dot(T.dot(F)) print initAffine moving = moving.get_data().squeeze().astype(np.float64) fixed = fixed.get_data().squeeze().astype(np.float64) moving = np.copy(moving, order='C') fixed = np.copy(fixed, order='C') moving = (moving - moving.min()) / (moving.max() - moving.min()) fixed = (fixed - fixed.min()) / (fixed.max() - fixed.min()) level = 2 maskMoving = moving > 0 maskFixed = fixed > 0 movingPyramid = [ img for img in rcommon.pyramid_gaussian_3D(moving, level, maskMoving) ] fixedPyramid = [ img for img in rcommon.pyramid_gaussian_3D(fixed, level, maskFixed) ] #maxOuterIter=[25,50,100,100, 100, 100] maxOuterIter = [2, 2, 2, 2, 2, 2] baseMoving = rcommon.getBaseFileName(fnameMoving) baseFixed = rcommon.getBaseFileName(fnameFixed) # if(os.path.exists('disp_'+baseMoving+'_'+baseFixed+'.npy')): # displacement=np.load('disp_'+baseMoving+'_'+baseFixed+'.npy') # else: displacement, directInverse = estimateMultimodalSyN3DMultiScale( movingPyramid, fixedPyramid, initAffine, lambdaParam, maxOuterIter, 0) tf.prepend_affine_to_displacement_field(displacement, initAffine) # np.save('disp_'+baseMoving+'_'+baseFixed+'.npy', displacement) #####Warp all requested volumes #---first the target using tri-linear interpolation--- moving = nib.load(fnameMoving).get_data().squeeze().astype(np.float64) moving = np.copy(moving, order='C') warped = np.array(tf.warp_volume(moving, displacement)).astype(np.int16) imgWarped = nib.Nifti1Image(warped, F) imgWarped.to_filename('warpedDiff_' + baseMoving + '_' + baseFixed + '.nii.gz') #---warp using affine only moving = nib.load(fnameMoving).get_data().squeeze().astype(np.int32) moving = np.copy(moving, order='C') warped = np.array( tf.warp_discrete_volumeNNAffine(moving, referenceShape, initAffine)).astype(np.int16) imgWarped = nib.Nifti1Image( warped, F) #The affine transformation is the reference's one imgWarped.to_filename('warpedAffine_' + baseMoving + '_' + baseFixed + '.nii.gz') #---now the rest of the targets using nearest neighbor names = [os.path.join(warpDir, name) for name in os.listdir(warpDir)] for name in names: #---warp using the non-linear deformation toWarp = nib.load(name).get_data().squeeze().astype(np.int32) toWarp = np.copy(toWarp, order='C') baseWarp = rcommon.getBaseFileName(name) warped = np.array(tf.warp_discrete_volumeNN( toWarp, displacement)).astype(np.int16) imgWarped = nib.Nifti1Image( warped, F) #The affine transformation is the reference's one imgWarped.to_filename('warpedDiff_' + baseWarp + '_' + baseFixed + '.nii.gz') #---warp using affine inly warped = np.array( tf.warp_discrete_volumeNNAffine(toWarp, referenceShape, initAffine)).astype(np.int16) imgWarped = nib.Nifti1Image( warped, F) #The affine transformation is the reference's one imgWarped.to_filename('warpedAffine_' + baseWarp + '_' + baseFixed + '.nii.gz') #---finally, the deformed lattices (forward, inverse and resdidual)--- lambdaParam = 0.9 maxIter = 100 tolerance = 1e-4 print 'Computing inverse...' inverse = np.array( tf.invert_vector_field3D(displacement, lambdaParam, maxIter, tolerance)) residual = np.array(tf.compose_vector_fields3D(displacement, inverse)) saveDeformedLattice3D( displacement, 'latticeDispDiff_' + baseMoving + '_' + baseFixed + '.nii.gz') saveDeformedLattice3D( inverse, 'latticeInvDiff_' + baseMoving + '_' + baseFixed + '.nii.gz') saveDeformedLattice3D( residual, 'latticeResdiff_' + baseMoving + '_' + baseFixed + '.nii.gz') residual = np.sqrt(np.sum(residual**2, 3)) print "Mean residual norm:", residual.mean(), " (", residual.std( ), "). Max residual norm:", residual.max()
def estimateNewMultimodalSyNField3D(moving, fixed, fWarp, fInv, mWarp, mInv, initAffine, lambdaDisplacement, quantizationLevels, maxOuterIter, reportProgress=False): ''' fwWarp: forward warp, the displacement field that warps moving towards fixed bwWarp: backward warp, the displacement field that warps fixed towards moving initAffine: the affine transformation to bring moving over fixed (this part is not symmetric) ''' print 'Moving shape:', moving.shape, '. Fixed shape:', fixed.shape innerTolerance = 1e-3 outerTolerance = 1e-3 fixedMask = (fixed > 0).astype(np.int32) movingMask = (moving > 0).astype(np.int32) if (fWarp != None): totalF = fWarp totalFInv = fInv else: totalF = np.zeros(shape=(fixed.shape) + (3, ), dtype=np.float64) totalFInv = np.zeros(shape=(fixed.shape) + (3, ), dtype=np.float64) if (mWarp != None): totalM = mWarp totalMInv = mInv else: totalM = np.zeros(shape=(moving.shape) + (3, ), dtype=np.float64) totalMInv = np.zeros(shape=(moving.shape) + (3, ), dtype=np.float64) finished = False outerIter = 0 while ((not finished) and (outerIter < maxOuterIter)): outerIter += 1 if (reportProgress): print 'Iter:', outerIter, '/', maxOuterIter #---E step--- wmoving = np.array(tf.warp_volume(moving, totalMInv, initAffine)) wmovingMask = np.array( tf.warp_discrete_volumeNN(movingMask, totalMInv, initAffine)).astype(np.int32) wfixed = np.array(tf.warp_volume(fixed, totalFInv)) wfixedMask = np.array(tf.warp_discrete_volumeNN( fixedMask, totalFInv)).astype(np.int32) fixedQ, grayLevels, hist = tf.quantizePositiveVolumeCYTHON( wfixed, quantizationLevels) fixedQ = np.array(fixedQ, dtype=np.int32) movingQ, grayLevels, hist = tf.quantizePositiveVolumeCYTHON( wmoving, quantizationLevels) movingQ = np.array(movingQ, dtype=np.int32) trust = wfixedMask * wmovingMask meansMoving, variancesMoving = tf.computeMaskedVolumeClassStatsCYTHON( trust, wmoving, quantizationLevels, fixedQ) meansFixed, variancesFixed = tf.computeMaskedVolumeClassStatsCYTHON( trust, wfixed, quantizationLevels, movingQ) meansMoving[0] = 0 meansFixed[0] = 0 meansMoving = np.array(meansMoving) meansFixed = np.array(meansFixed) variancesMoving = np.array(variancesMoving) sigmaFieldMoving = variancesMoving[fixedQ] variancesFixed = np.array(variancesFixed) sigmaFieldFixed = variancesFixed[movingQ] deltaFieldMoving = meansMoving[fixedQ] - wmoving deltaFieldFixed = meansFixed[movingQ] - wfixed #--M step-- movingGradient = np.empty(shape=(moving.shape) + (3, ), dtype=np.float64) movingGradient[:, :, :, 0], movingGradient[:, :, :, 1], movingGradient[:, :, :, 2] = sp.gradient( wmoving) #iterate forward field maxVariation = 1 + innerTolerance innerIter = 0 maxInnerIter = 100 fw = np.zeros_like(totalF) while ((maxVariation > innerTolerance) and (innerIter < maxInnerIter)): innerIter += 1 maxVariation = tf.iterateDisplacementField3DCYTHON( deltaFieldMoving, sigmaFieldMoving, movingGradient, lambdaDisplacement, totalF, fw, None) del movingGradient fw *= 0.5 totalF = np.array(tf.compose_vector_fields3D( fw, totalF)) #Multiply fw by 0.5?? nrm = np.sqrt(fw[..., 0]**2 + fw[..., 1]**2 + fw[..., 2]**2) del fw #iterate backward field fixedGradient = np.empty(shape=(fixed.shape) + (3, ), dtype=np.float64) fixedGradient[:, :, :, 0], fixedGradient[:, :, :, 1], fixedGradient[:, :, :, 2] = sp.gradient( wfixed) maxVariation = 1 + innerTolerance innerIter = 0 maxInnerIter = 100 mw = np.zeros_like(totalM) while ((maxVariation > innerTolerance) and (innerIter < maxInnerIter)): innerIter += 1 maxVariation = tf.iterateDisplacementField3DCYTHON( deltaFieldFixed, sigmaFieldFixed, fixedGradient, lambdaDisplacement, totalM, mw, None) del fixedGradient mw *= 0.5 totalM = np.array(tf.compose_vector_fields3D( mw, totalM)) #Multiply bw by 0.5?? nrm = np.sqrt(mw[..., 0]**2 + mw[..., 1]**2 + mw[..., 2]**2) del mw #invert fields totalFInv = np.array( tf.invert_vector_field_fixed_point3D(totalF, 20, 1e-6)) totalMInv = np.array( tf.invert_vector_field_fixed_point3D(totalM, 20, 1e-6)) totalF = np.array( tf.invert_vector_field_fixed_point3D(totalFInv, 20, 1e-6)) totalM = np.array( tf.invert_vector_field_fixed_point3D(totalMInv, 20, 1e-6)) maxDisplacement = np.mean(nrm) if ((maxDisplacement < outerTolerance) or (outerIter >= maxOuterIter)): finished = True print "Iter: ", outerIter, "Mean displacement:", maxDisplacement, "Max variation:", maxVariation return totalF, totalFInv, totalM, totalMInv
def estimateNewMultimodalDiffeomorphicField3D(moving, fixed, initAffine, lambdaDisplacement, quantizationLevels, maxOuterIter, previousDisplacement, reportProgress=False): innerTolerance=1e-3 outerTolerance=1e-3 displacement =np.empty(shape=(fixed.shape)+(3,), dtype=np.float64) residuals=np.zeros(shape=(fixed.shape), dtype=np.float64) gradientField =np.empty(shape=(fixed.shape)+(3,), dtype=np.float64) totalDisplacement=np.zeros(shape=(fixed.shape)+(3,), dtype=np.float64) if(previousDisplacement!=None): totalDisplacement[...]=previousDisplacement fixedQ=None grayLevels=None fixedQ, grayLevels, hist=tf.quantizePositiveVolumeCYTHON(fixed, quantizationLevels) fixedQ=np.array(fixedQ, dtype=np.int32) finished=False outerIter=0 maxDisplacement=None maxVariation=None maxResidual=0 fixedMask=(fixed>0).astype(np.int32) movingMask=(moving>0).astype(np.int32) trustRegion=fixedMask*np.array(tf.warp_discrete_volumeNNAffine(movingMask, np.array(fixedMask.shape, dtype=np.int32), initAffine))#consider only the overlap after affine registration while((not finished) and (outerIter<maxOuterIter)): outerIter+=1 if(reportProgress): print 'Iter:',outerIter,'/',maxOuterIter #sys.stdout.flush() #---E step--- #print "Warping..." #sys.stdout.flush() warped=np.array(tf.warp_volume(moving, totalDisplacement, initAffine)) warpedMask=np.array(tf.warp_discrete_volumeNN(trustRegion, totalDisplacement, np.eye(4))).astype(np.int32)#the affine mapping was already applied #print "Warping NN..." #sys.stdout.flush() #warpedMovingMask=np.array(tf.warp_volumeNN(movingMask, totalDisplacement)).astype(np.int32) #print "Class stats..." #sys.stdout.flush() means, variances=tf.computeMaskedVolumeClassStatsCYTHON(warpedMask, warped, quantizationLevels, fixedQ) means[0]=0 means=np.array(means) variances=np.array(variances) sigmaField=variances[fixedQ] deltaField=means[fixedQ]-warped#########Delta-field using Arce's rule #--M step-- g0, g1, g2=sp.gradient(warped) gradientField[:,:,:,0]=g0 gradientField[:,:,:,1]=g1 gradientField[:,:,:,2]=g2 maxVariation=1+innerTolerance innerIter=0 maxInnerIter=100 displacement[...]=0 #print "Iterating..." #sys.stdout.flush() while((maxVariation>innerTolerance)and(innerIter<maxInnerIter)): innerIter+=1 maxVariation=tf.iterateDisplacementField3DCYTHON(deltaField, sigmaField, gradientField, lambdaDisplacement, totalDisplacement, displacement, residuals) opt=np.max(residuals) if(maxResidual<opt): maxResidual=opt #--accumulate displacement-- #print "Exponential3D. Range D:", displacement.min(), displacement.max() #sys.stdout.flush() expd, inverseNone=tf.vector_field_exponential3D(displacement, False) expd=np.array(expd) #print "Range expd:", expd.min(), expd.max(), "Range TD:", totalDisplacement.min(), totalDisplacement.max() #print "Compose vector fields..." #sys.stdout.flush() totalDisplacement, stats=tf.compose_vector_fields3D(expd, totalDisplacement) totalDisplacement=np.array(totalDisplacement) #print "Composed rage:", totalDisplacement.min(), totalDisplacement.max() #sys.stdout.flush() #--check stop condition-- nrm=np.sqrt(displacement[...,0]**2+displacement[...,1]**2+displacement[...,2]**2) #maxDisplacement=np.max(nrm) maxDisplacement=np.mean(nrm) if((maxDisplacement<outerTolerance)or(outerIter>=maxOuterIter)): finished=True print "Iter: ",outerIter, "Mean displacement:", maxDisplacement, "Max variation:",maxVariation, "Max residual:", maxResidual #sh=fixed.shape #rcommon.overlayImages(warped[:,sh[1]//2,:], fixed[:,sh[1]//2,:]) #rcommon.overlayImages(warped[:,sh[1]//2,:]*warpedMask[:,sh[1]//2,:], fixed[:,sh[1]//2,:]) #sys.stdout.flush() if(previousDisplacement!=None): #print 'Range TD:', totalDisplacement.min(), totalDisplacement.max(),'. Range PD:', previousDisplacement.min(), previousDisplacement.max() #sys.stdout.flush() return totalDisplacement-previousDisplacement return totalDisplacement
def testEstimateMultimodalSyN3DMultiScale(fnameMoving, fnameFixed, fnameAffine, warpDir, lambdaParam): ''' testEstimateMultimodalDiffeomorphicField3DMultiScale('IBSR_01_ana_strip.nii.gz', 't1_icbm_normal_1mm_pn0_rf0_peeled.nii.gz', 'IBSR_01_ana_strip_t1_icbm_normal_1mm_pn0_rf0_peeledAffine.txt', 100) ''' print 'Registering', fnameMoving, 'to', fnameFixed,'with lambda=',lambdaParam sys.stdout.flush() moving = nib.load(fnameMoving) fixed= nib.load(fnameFixed) referenceShape=np.array(fixed.shape, dtype=np.int32) M=moving.get_affine() F=fixed.get_affine() if not fnameAffine: T=np.eye(4) else: T=rcommon.readAntsAffine(fnameAffine) initAffine=np.linalg.inv(M).dot(T.dot(F)) print initAffine moving=moving.get_data().squeeze().astype(np.float64) fixed=fixed.get_data().squeeze().astype(np.float64) moving=np.copy(moving, order='C') fixed=np.copy(fixed, order='C') moving=(moving-moving.min())/(moving.max()-moving.min()) fixed=(fixed-fixed.min())/(fixed.max()-fixed.min()) level=2 maskMoving=moving>0 maskFixed=fixed>0 movingPyramid=[img for img in rcommon.pyramid_gaussian_3D(moving, level, maskMoving)] fixedPyramid=[img for img in rcommon.pyramid_gaussian_3D(fixed, level, maskFixed)] #maxOuterIter=[25,50,100,100, 100, 100] maxOuterIter=[2,2,2,2,2,2] baseMoving=rcommon.getBaseFileName(fnameMoving) baseFixed=rcommon.getBaseFileName(fnameFixed) # if(os.path.exists('disp_'+baseMoving+'_'+baseFixed+'.npy')): # displacement=np.load('disp_'+baseMoving+'_'+baseFixed+'.npy') # else: displacement, directInverse=estimateMultimodalSyN3DMultiScale(movingPyramid, fixedPyramid, initAffine, lambdaParam, maxOuterIter, 0) tf.prepend_affine_to_displacement_field(displacement, initAffine) # np.save('disp_'+baseMoving+'_'+baseFixed+'.npy', displacement) #####Warp all requested volumes #---first the target using tri-linear interpolation--- moving=nib.load(fnameMoving).get_data().squeeze().astype(np.float64) moving=np.copy(moving, order='C') warped=np.array(tf.warp_volume(moving, displacement)).astype(np.int16) imgWarped=nib.Nifti1Image(warped, F) imgWarped.to_filename('warpedDiff_'+baseMoving+'_'+baseFixed+'.nii.gz') #---warp using affine only moving=nib.load(fnameMoving).get_data().squeeze().astype(np.int32) moving=np.copy(moving, order='C') warped=np.array(tf.warp_discrete_volumeNNAffine(moving, referenceShape, initAffine)).astype(np.int16) imgWarped=nib.Nifti1Image(warped, F)#The affine transformation is the reference's one imgWarped.to_filename('warpedAffine_'+baseMoving+'_'+baseFixed+'.nii.gz') #---now the rest of the targets using nearest neighbor names=[os.path.join(warpDir,name) for name in os.listdir(warpDir)] for name in names: #---warp using the non-linear deformation toWarp=nib.load(name).get_data().squeeze().astype(np.int32) toWarp=np.copy(toWarp, order='C') baseWarp=rcommon.getBaseFileName(name) warped=np.array(tf.warp_discrete_volumeNN(toWarp, displacement)).astype(np.int16) imgWarped=nib.Nifti1Image(warped, F)#The affine transformation is the reference's one imgWarped.to_filename('warpedDiff_'+baseWarp+'_'+baseFixed+'.nii.gz') #---warp using affine inly warped=np.array(tf.warp_discrete_volumeNNAffine(toWarp, referenceShape, initAffine)).astype(np.int16) imgWarped=nib.Nifti1Image(warped, F)#The affine transformation is the reference's one imgWarped.to_filename('warpedAffine_'+baseWarp+'_'+baseFixed+'.nii.gz') #---finally, the deformed lattices (forward, inverse and resdidual)--- lambdaParam=0.9 maxIter=100 tolerance=1e-4 print 'Computing inverse...' inverse=np.array(tf.invert_vector_field3D(displacement, lambdaParam, maxIter, tolerance)) residual=np.array(tf.compose_vector_fields3D(displacement, inverse)) saveDeformedLattice3D(displacement, 'latticeDispDiff_'+baseMoving+'_'+baseFixed+'.nii.gz') saveDeformedLattice3D(inverse, 'latticeInvDiff_'+baseMoving+'_'+baseFixed+'.nii.gz') saveDeformedLattice3D(residual, 'latticeResdiff_'+baseMoving+'_'+baseFixed+'.nii.gz') residual=np.sqrt(np.sum(residual**2,3)) print "Mean residual norm:", residual.mean()," (",residual.std(), "). Max residual norm:", residual.max()
def estimateNewMultimodalSyNField3D(moving, fixed, fWarp, fInv, mWarp, mInv, initAffine, lambdaDisplacement, quantizationLevels, maxOuterIter, reportProgress=False): ''' fwWarp: forward warp, the displacement field that warps moving towards fixed bwWarp: backward warp, the displacement field that warps fixed towards moving initAffine: the affine transformation to bring moving over fixed (this part is not symmetric) ''' print 'Moving shape:',moving.shape,'. Fixed shape:',fixed.shape innerTolerance=1e-3 outerTolerance=1e-3 fixedMask=(fixed>0).astype(np.int32) movingMask=(moving>0).astype(np.int32) if(fWarp!=None): totalF=fWarp totalFInv=fInv else: totalF =np.zeros(shape=(fixed.shape)+(3,), dtype=np.float64) totalFInv =np.zeros(shape=(fixed.shape)+(3,), dtype=np.float64) if(mWarp!=None): totalM=mWarp totalMInv=mInv else: totalM =np.zeros(shape=(moving.shape)+(3,), dtype=np.float64) totalMInv=np.zeros(shape=(moving.shape)+(3,), dtype=np.float64) finished=False outerIter=0 while((not finished) and (outerIter<maxOuterIter)): outerIter+=1 if(reportProgress): print 'Iter:',outerIter,'/',maxOuterIter #---E step--- wmoving=np.array(tf.warp_volume(moving, totalMInv, initAffine)) wmovingMask=np.array(tf.warp_discrete_volumeNN(movingMask, totalMInv, initAffine)).astype(np.int32) wfixed=np.array(tf.warp_volume(fixed, totalFInv)) wfixedMask=np.array(tf.warp_discrete_volumeNN(fixedMask, totalFInv)).astype(np.int32) fixedQ, grayLevels, hist=tf.quantizePositiveVolumeCYTHON(wfixed, quantizationLevels) fixedQ=np.array(fixedQ, dtype=np.int32) movingQ, grayLevels, hist=tf.quantizePositiveVolumeCYTHON(wmoving, quantizationLevels) movingQ=np.array(movingQ, dtype=np.int32) trust=wfixedMask*wmovingMask meansMoving, variancesMoving=tf.computeMaskedVolumeClassStatsCYTHON(trust, wmoving, quantizationLevels, fixedQ) meansFixed, variancesFixed=tf.computeMaskedVolumeClassStatsCYTHON(trust, wfixed, quantizationLevels, movingQ) meansMoving[0]=0 meansFixed[0]=0 meansMoving=np.array(meansMoving) meansFixed=np.array(meansFixed) variancesMoving=np.array(variancesMoving) sigmaFieldMoving=variancesMoving[fixedQ] variancesFixed=np.array(variancesFixed) sigmaFieldFixed=variancesFixed[movingQ] deltaFieldMoving=meansMoving[fixedQ]-wmoving deltaFieldFixed=meansFixed[movingQ]-wfixed #--M step-- movingGradient =np.empty(shape=(moving.shape)+(3,), dtype=np.float64) movingGradient[:,:,:,0], movingGradient[:,:,:,1], movingGradient[:,:,:,2]=sp.gradient(wmoving) #iterate forward field maxVariation=1+innerTolerance innerIter=0 maxInnerIter=100 fw=np.zeros_like(totalF) while((maxVariation>innerTolerance)and(innerIter<maxInnerIter)): innerIter+=1 maxVariation=tf.iterateDisplacementField3DCYTHON(deltaFieldMoving, sigmaFieldMoving, movingGradient, lambdaDisplacement, totalF, fw, None) del movingGradient fw*=0.5 totalF=np.array(tf.compose_vector_fields3D(fw, totalF))#Multiply fw by 0.5?? nrm=np.sqrt(fw[...,0]**2+fw[...,1]**2+fw[...,2]**2) del fw #iterate backward field fixedGradient =np.empty(shape=(fixed.shape)+(3,), dtype=np.float64) fixedGradient[:,:,:,0], fixedGradient[:,:,:,1], fixedGradient[:,:,:,2]=sp.gradient(wfixed) maxVariation=1+innerTolerance innerIter=0 maxInnerIter=100 mw=np.zeros_like(totalM) while((maxVariation>innerTolerance)and(innerIter<maxInnerIter)): innerIter+=1 maxVariation=tf.iterateDisplacementField3DCYTHON(deltaFieldFixed, sigmaFieldFixed, fixedGradient, lambdaDisplacement, totalM, mw, None) del fixedGradient mw*=0.5 totalM=np.array(tf.compose_vector_fields3D(mw, totalM))#Multiply bw by 0.5?? nrm=np.sqrt(mw[...,0]**2+mw[...,1]**2+mw[...,2]**2) del mw #invert fields totalFInv=np.array(tf.invert_vector_field_fixed_point3D(totalF, 20, 1e-6)) totalMInv=np.array(tf.invert_vector_field_fixed_point3D(totalM, 20, 1e-6)) totalF=np.array(tf.invert_vector_field_fixed_point3D(totalFInv, 20, 1e-6)) totalM=np.array(tf.invert_vector_field_fixed_point3D(totalMInv, 20, 1e-6)) maxDisplacement=np.mean(nrm) if((maxDisplacement<outerTolerance)or(outerIter>=maxOuterIter)): finished=True print "Iter: ",outerIter, "Mean displacement:", maxDisplacement, "Max variation:",maxVariation return totalF, totalFInv, totalM, totalMInv