def compute(self): all_data = self.getData('data') xml_header = self.getData('ISMRMRDHeader') header = ismrmrd.xsd.CreateFromDocument(xml_header) enc = header.encoding[0] #Parallel imaging factor acc_factor = 1 if enc.parallelImaging: acc_factor = enc.parallelImaging.accelerationFactor.kspace_encoding_step_1 # Coil combination print "Calculating coil images and CSM" coil_images = transform.transform_kspace_to_image(np.squeeze(np.mean(all_data,0)),(1,2)) (csm,rho) = coils.calculate_csm_walsh(coil_images) csm_ss = np.sum(csm * np.conj(csm),0) csm_ss = csm_ss + 1.0*(csm_ss < np.spacing(1)).astype('float32') if acc_factor > 1: coil_data = np.squeeze(np.mean(all_data,0)) if self.getVal('Parallel Imaging Method') == 0: (unmix,gmap) = grappa.calculate_grappa_unmixing(coil_data, acc_factor,csm=csm) elif self.getVal('Parallel Imaging Method') == 1: (unmix,gmap) = sense.calculate_sense_unmixing(acc_factor,csm) else: raise Exception('Unknown parallel imaging method') recon = np.zeros((all_data.shape[-4],all_data.shape[-2],all_data.shape[-1]), dtype=np.complex64) for r in range(0,all_data.shape[-4]): recon_data = transform.transform_kspace_to_image(np.squeeze(all_data[r,:,:,:]),(1,2))*np.sqrt(acc_factor) if acc_factor > 1: recon[r,:,:] = np.sum(unmix * recon_data,0) else: recon[r,:,:] = np.sum(np.conj(csm) * recon_data,0) print "Reconstruction done" self.setData('recon', recon) if acc_factor == 1: gmap = np.ones((all_data.shape[-2],all_data.shape[-1]),dtype=np.float32) self.setData('gmap',gmap) return 0
#%% # Apply prewhitening kspace = coils.apply_prewhitening(kspace, dmtx) data = coils.apply_prewhitening(data, dmtx) #%% #Reconstruct aliased images alias_img = transform.transform_kspace_to_image(kspace,dim=(1,2)) * np.sqrt(acc_factor) show.imshow(abs(alias_img)) #%% reload(sense) (unmix_sense, gmap_sense) = sense.calculate_sense_unmixing(acc_factor,csm) show.imshow(abs(gmap_sense),colorbar=True) recon_sense = np.squeeze(np.sum(alias_img * unmix_sense,0)) show.imshow(abs(recon_sense),colorbar=True) #%% reload(grappa) #(unmix_grappa,gmap_grappa) = grappa.calculate_grappa_unmixing(data, acc_factor, data_mask=pat>1, csm=csm,kernel_size=(2,5)) (unmix_grappa,gmap_grappa) = grappa.calculate_grappa_unmixing(data, acc_factor, data_mask=pat>1,kernel_size=(2,5)) show.imshow(abs(gmap_grappa),colorbar=True) recon_grappa = np.squeeze(np.sum(alias_img * unmix_grappa,0)) show.imshow(abs(recon_grappa),colorbar=True) #%% #Pseudo replica example reps = 255
def reconstruct_epi(filename, datasetname, noise, gre): # Read the epi data dset = ismrmrd.Dataset(filename,datasetname) ############################## # Scan Parameters and Layout # ############################## header = ismrmrd.xsd.CreateFromDocument(dset.read_xml_header()) enc = header.encoding[0] nkx = enc.encodedSpace.matrixSize.x nky = enc.encodedSpace.matrixSize.y ncoils = header.acquisitionSystemInformation.receiverChannels epi_noise_bw = header.acquisitionSystemInformation.relativeReceiverNoiseBandwidth acc_factor = enc.parallelImaging.accelerationFactor.kspace_encoding_step_1 # Number of Slices if enc.encodingLimits.slice != None: nslices = enc.encodingLimits.slice.maximum + 1 else: nslices = 1 # Loop through the acquisitions ignoring the noise scans and the # parallel imaging calibration scans which are EPI based firstscan = 0 while True: acq = dset.read_acquisition(firstscan) if acq.isFlagSet(ismrmrd.ACQ_IS_NOISE_MEASUREMENT) or acq.isFlagSet(ismrmrd.ACQ_IS_PARALLEL_CALIBRATION): firstscan += 1 else: break #print('First imaging scan at:', firstscan) nsamp = acq.number_of_samples ncoils = acq.active_channels sampletime = acq.sample_time_us # The lines are labeled with flags as follows: # - Noise or Imaging using ACQ_IS_NOISE_MEASUREMENT # - Parallel calibration using ACQ_IS_PARALLEL_CALIBRATION # - Forward or Reverse using the ACQ_IS_REVERSE flag # - EPI navigator using ACQ_IS_PHASECORR_DATA # - First or last in a slice using ACQ_FIRST_IN_SLICE and ACQ_LAST_IN_SLICE # - The first navigator in a shot is labeled as first in slice # - The first imaging line in a shot is labeled as firt in slice # - The last imaging line in a show is labeled as last in slice # for n in range(firstscan-1,firstscan+60): # acq = dset.read_acquisition(n) # print(acq.idx.kspace_encode_step_1) # if acq.isFlagSet(ismrmrd.ACQ_FIRST_IN_SLICE): # print('First') # elif acq.isFlagSet(ismrmrd.ACQ_LAST_IN_SLICE): # print('Last') # else: # print('Middle') # if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): # print('Reverse') # else: # print('Forward') # if acq.isFlagSet(ismrmrd.ACQ_IS_PHASECORR_DATA): # print('Navigator') # The EPI trajectory is described in the XML header # for o in enc.trajectoryDescription.userParameterLong: # print(o.name, o.value_) # # for o in enc.trajectoryDescription.userParameterDouble: # print(o.name, o.value_) tup = tdown = tflat = tdelay = nsamp = nnav = etl = 0 for o in enc.trajectoryDescription.userParameterLong: if o.name == 'rampUpTime': tup = o.value_ if o.name == 'rampDownTime': tdown = o.value_ if o.name == 'flatTopTime': tflat = o.value_ if o.name == 'acqDelayTime': tdelay = o.value_ if o.name == 'numSamples': nsamp = o.value_ if o.name == 'numberOfNavigators': nnav = o.value_ if o.name == 'etl': etl = o.value_ #print(tup, tdown, tflat, tdelay, nsamp, nnav, etl) #################################### # Calculate the gridding operators # #################################### nkx = enc.encodedSpace.matrixSize.x nx = enc.reconSpace.matrixSize.x t = tdelay + sampletime*np.arange(nsamp) x = np.arange(nx)/nx-0.5 up = t<=tup flat = (t>tup)*(t<(tup+tflat)) down = t>=(tup+tflat) #Integral of trajectory (Gmax=1.0) k = np.zeros(nsamp) k[up] = 0.5/tup*t[up]**2 k[flat] = 0.5*tup + (t[flat] - tup) k[down] = 0.5*tup + tflat + 0.5*tdown-0.5/tdown*(tup+tflat+tdown-t[down])**2 #Scale to match resolution k *= nkx/(k[-1]-k[0]) #Center k -= k[nsamp//2] kpos = k kneg = -1.0*k #Corresponding even range keven = np.arange(nkx) keven -= keven[nkx//2] #Forward model Qpos = np.zeros([nsamp,nkx]) Qneg = np.zeros([nsamp,nkx]) for p in range(nsamp): Qpos[p,:] = np.sinc(kpos[p]-keven) Qneg[p,:] = np.sinc(kneg[p]-keven) #Inverse Rpos = np.linalg.pinv(Qpos) Rneg = np.linalg.pinv(Qneg) #Take transpose because we apply from the right Rpos = Rpos.transpose() Rneg = Rneg.transpose() ################################# # Calculate the kspace filter # # Hanning filter after gridding # ################################# import scipy.signal kfiltx = scipy.signal.hann(nkx) kfilty = scipy.signal.hann(nky) Rpos = np.dot(Rpos, np.diag(kfiltx)) Rneg = np.dot(Rneg, np.diag(kfiltx)) #################################### # Calculate SENSE unmixing weights # #################################### # Some basic checks if gre.shape[0] != nslices: raise ValueError('Calibration and EPI data have different number of slices') if gre.shape[1] != ncoils: raise ValueError('Calibration and EPI data have different number of coils') # Estimate coil sensitivites from the GRE data csm_orig = np.zeros(gre.shape,dtype=np.complex) for z in range(nslices): (csmtmp, actmp, rhotmp) = coils.calculate_csm_inati_iter(gre[z,:,:,:]) weight = rhotmp**2 / (rhotmp**2 + .01*np.median(rhotmp.ravel())**2) csm_orig[z,:,:,:] = csmtmp*weight # Deal with difference in resolution # Up/down sample the coil sensitivities to the resolution of the EPI xcsm = np.arange(gre.shape[3])/gre.shape[3] ycsm = np.arange(gre.shape[2])/gre.shape[2] xepi = np.arange(nx)/nx yepi = np.arange(nky)/nky csm = np.zeros([nslices,ncoils,nky,nx],dtype=np.complex) for z in range(nslices): for c in range(ncoils): # interpolate the real part and imaginary part separately i_real = interp.RectBivariateSpline(ycsm,xcsm,np.real(csm_orig[z,c,:,:])) i_imag = interp.RectBivariateSpline(ycsm,xcsm,np.imag(csm_orig[z,c,:,:])) csm[z,c,:,:] = i_real(yepi,xepi) + 1j*i_imag(yepi,xepi) # SENSE weights unmix = np.zeros(csm.shape,dtype=np.complex) for z in range(nslices): unmix[z,:,:,:] = sense.calculate_sense_unmixing(acc_factor, csm[z,:,:,:])[0] ############### # Reconstruct # ############### # Initialize the array for a volume's worth of data H = np.zeros([nslices, ncoils, nky, nx],dtype=np.complex) # Loop over the slices scan = firstscan for z in range(nslices): #print('Slice %d starts at scan %d.'%(z,scan)) # Navigator 1 acq = dset.read_acquisition(scan) #print(scan,acq.idx.slice,acq.idx.kspace_encode_step_1,acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE)) currslice = acq.idx.slice # keep track of the slice number data = coils.apply_prewhitening(acq.data,noise.preWMtx) if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): rnav1 = transform.transform_kspace_to_image(np.dot(data, Rneg),dim=[1]) sgn = -1.0 else: rnav1 = transform.transform_kspace_to_image(np.dot(data, Rpos),dim=[1]) sgn = 1.0 scan += 1 # Navigator 2 acq = dset.read_acquisition(scan) #print(scan,acq.idx.slice,acq.idx.kspace_encode_step_1,acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE)) data = coils.apply_prewhitening(acq.data,noise.preWMtx) if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): rnav2 = transform.transform_kspace_to_image(np.dot(data, Rneg),dim=[1]) else: rnav2 = transform.transform_kspace_to_image(np.dot(data, Rpos),dim=[1]) scan += 1 # Navigator 3 acq = dset.read_acquisition(scan) #print(scan,acq.idx.slice,acq.idx.kspace_encode_step_1,acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE)) data = coils.apply_prewhitening(acq.data,noise.preWMtx) if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): rnav3 = transform.transform_kspace_to_image(np.dot(data, Rneg),dim=[1]) else: rnav3 = transform.transform_kspace_to_image(np.dot(data, Rpos),dim=[1]) scan += 1 # Phase correction delta = np.conj(rnav1+rnav3) * rnav2 fdelta = np.tile(np.mean(delta,axis=0),[ncoils,1]) corr = np.exp(sgn*1j*np.angle(np.sqrt(fdelta))) for j in range(nky): acq = dset.read_acquisition(scan) slice = acq.idx.slice if slice != currslice: # end of this slice break ky = acq.idx.kspace_encode_step_1 data = coils.apply_prewhitening(acq.data,noise.preWMtx) if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): rho = transform.transform_kspace_to_image(np.dot(data, Rneg),dim=[1]) H[slice,:,ky,:] = kfilty[ky]*np.conj(corr)*rho else: rho = transform.transform_kspace_to_image(np.dot(data, Rpos),dim=[1]) H[slice,:,ky,:] = kfilty[ky]*corr*rho scan += 1 # Close the data set dset.close() # Recon in along y H = transform.transform_kspace_to_image(H,dim=[2]) # Combine with SENSE weights epi_im = np.abs(np.squeeze(np.sum(H*unmix,axis=1))) return epi_im
def reconstruct_epi(filename, datasetname, noise, gre): # Read the epi data dset = ismrmrd.Dataset(filename, datasetname) ############################## # Scan Parameters and Layout # ############################## header = ismrmrd.xsd.CreateFromDocument(dset.read_xml_header()) enc = header.encoding[0] nkx = enc.encodedSpace.matrixSize.x nky = enc.encodedSpace.matrixSize.y ncoils = header.acquisitionSystemInformation.receiverChannels epi_noise_bw = header.acquisitionSystemInformation.relativeReceiverNoiseBandwidth acc_factor = enc.parallelImaging.accelerationFactor.kspace_encoding_step_1 # Number of Slices if enc.encodingLimits.slice != None: nslices = enc.encodingLimits.slice.maximum + 1 else: nslices = 1 # Loop through the acquisitions ignoring the noise scans and the # parallel imaging calibration scans which are EPI based firstscan = 0 while True: acq = dset.read_acquisition(firstscan) if acq.isFlagSet(ismrmrd.ACQ_IS_NOISE_MEASUREMENT) or acq.isFlagSet( ismrmrd.ACQ_IS_PARALLEL_CALIBRATION): firstscan += 1 else: break #print('First imaging scan at:', firstscan) nsamp = acq.number_of_samples ncoils = acq.active_channels sampletime = acq.sample_time_us # The lines are labeled with flags as follows: # - Noise or Imaging using ACQ_IS_NOISE_MEASUREMENT # - Parallel calibration using ACQ_IS_PARALLEL_CALIBRATION # - Forward or Reverse using the ACQ_IS_REVERSE flag # - EPI navigator using ACQ_IS_PHASECORR_DATA # - First or last in a slice using ACQ_FIRST_IN_SLICE and ACQ_LAST_IN_SLICE # - The first navigator in a shot is labeled as first in slice # - The first imaging line in a shot is labeled as firt in slice # - The last imaging line in a show is labeled as last in slice # for n in range(firstscan-1,firstscan+60): # acq = dset.read_acquisition(n) # print(acq.idx.kspace_encode_step_1) # if acq.isFlagSet(ismrmrd.ACQ_FIRST_IN_SLICE): # print('First') # elif acq.isFlagSet(ismrmrd.ACQ_LAST_IN_SLICE): # print('Last') # else: # print('Middle') # if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): # print('Reverse') # else: # print('Forward') # if acq.isFlagSet(ismrmrd.ACQ_IS_PHASECORR_DATA): # print('Navigator') # The EPI trajectory is described in the XML header # for o in enc.trajectoryDescription.userParameterLong: # print(o.name, o.value_) # # for o in enc.trajectoryDescription.userParameterDouble: # print(o.name, o.value_) tup = tdown = tflat = tdelay = nsamp = nnav = etl = 0 for o in enc.trajectoryDescription.userParameterLong: if o.name == 'rampUpTime': tup = o.value_ if o.name == 'rampDownTime': tdown = o.value_ if o.name == 'flatTopTime': tflat = o.value_ if o.name == 'acqDelayTime': tdelay = o.value_ if o.name == 'numSamples': nsamp = o.value_ if o.name == 'numberOfNavigators': nnav = o.value_ if o.name == 'etl': etl = o.value_ #print(tup, tdown, tflat, tdelay, nsamp, nnav, etl) #################################### # Calculate the gridding operators # #################################### nkx = enc.encodedSpace.matrixSize.x nx = enc.reconSpace.matrixSize.x t = tdelay + sampletime * np.arange(nsamp) x = np.arange(nx) / nx - 0.5 up = t <= tup flat = (t > tup) * (t < (tup + tflat)) down = t >= (tup + tflat) #Integral of trajectory (Gmax=1.0) k = np.zeros(nsamp) k[up] = 0.5 / tup * t[up]**2 k[flat] = 0.5 * tup + (t[flat] - tup) k[down] = 0.5 * tup + tflat + 0.5 * tdown - 0.5 / tdown * ( tup + tflat + tdown - t[down])**2 #Scale to match resolution k *= nkx / (k[-1] - k[0]) #Center k -= k[nsamp // 2] kpos = k kneg = -1.0 * k #Corresponding even range keven = np.arange(nkx) keven -= keven[nkx // 2] #Forward model Qpos = np.zeros([nsamp, nkx]) Qneg = np.zeros([nsamp, nkx]) for p in range(nsamp): Qpos[p, :] = np.sinc(kpos[p] - keven) Qneg[p, :] = np.sinc(kneg[p] - keven) #Inverse Rpos = np.linalg.pinv(Qpos) Rneg = np.linalg.pinv(Qneg) #Take transpose because we apply from the right Rpos = Rpos.transpose() Rneg = Rneg.transpose() ################################# # Calculate the kspace filter # # Hanning filter after gridding # ################################# import scipy.signal kfiltx = scipy.signal.hann(nkx) kfilty = scipy.signal.hann(nky) Rpos = np.dot(Rpos, np.diag(kfiltx)) Rneg = np.dot(Rneg, np.diag(kfiltx)) #################################### # Calculate SENSE unmixing weights # #################################### # Some basic checks if gre.shape[0] != nslices: raise ValueError( 'Calibration and EPI data have different number of slices') if gre.shape[1] != ncoils: raise ValueError( 'Calibration and EPI data have different number of coils') # Estimate coil sensitivites from the GRE data csm_orig = np.zeros(gre.shape, dtype=np.complex) for z in range(nslices): (csmtmp, actmp, rhotmp) = coils.calculate_csm_inati_iter(gre[z, :, :, :]) weight = rhotmp**2 / (rhotmp**2 + .01 * np.median(rhotmp.ravel())**2) csm_orig[z, :, :, :] = csmtmp * weight # Deal with difference in resolution # Up/down sample the coil sensitivities to the resolution of the EPI xcsm = np.arange(gre.shape[3]) / gre.shape[3] ycsm = np.arange(gre.shape[2]) / gre.shape[2] xepi = np.arange(nx) / nx yepi = np.arange(nky) / nky csm = np.zeros([nslices, ncoils, nky, nx], dtype=np.complex) for z in range(nslices): for c in range(ncoils): # interpolate the real part and imaginary part separately i_real = interp.RectBivariateSpline(ycsm, xcsm, np.real(csm_orig[z, c, :, :])) i_imag = interp.RectBivariateSpline(ycsm, xcsm, np.imag(csm_orig[z, c, :, :])) csm[z, c, :, :] = i_real(yepi, xepi) + 1j * i_imag(yepi, xepi) # SENSE weights unmix = np.zeros(csm.shape, dtype=np.complex) for z in range(nslices): unmix[z, :, :, :] = sense.calculate_sense_unmixing( acc_factor, csm[z, :, :, :])[0] ############### # Reconstruct # ############### # Initialize the array for a volume's worth of data H = np.zeros([nslices, ncoils, nky, nx], dtype=np.complex) # Loop over the slices scan = firstscan for z in range(nslices): #print('Slice %d starts at scan %d.'%(z,scan)) # Navigator 1 acq = dset.read_acquisition(scan) #print(scan,acq.idx.slice,acq.idx.kspace_encode_step_1,acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE)) currslice = acq.idx.slice # keep track of the slice number data = coils.apply_prewhitening(acq.data, noise.preWMtx) if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): rnav1 = transform.transform_kspace_to_image(np.dot(data, Rneg), dim=[1]) sgn = -1.0 else: rnav1 = transform.transform_kspace_to_image(np.dot(data, Rpos), dim=[1]) sgn = 1.0 scan += 1 # Navigator 2 acq = dset.read_acquisition(scan) #print(scan,acq.idx.slice,acq.idx.kspace_encode_step_1,acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE)) data = coils.apply_prewhitening(acq.data, noise.preWMtx) if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): rnav2 = transform.transform_kspace_to_image(np.dot(data, Rneg), dim=[1]) else: rnav2 = transform.transform_kspace_to_image(np.dot(data, Rpos), dim=[1]) scan += 1 # Navigator 3 acq = dset.read_acquisition(scan) #print(scan,acq.idx.slice,acq.idx.kspace_encode_step_1,acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE)) data = coils.apply_prewhitening(acq.data, noise.preWMtx) if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): rnav3 = transform.transform_kspace_to_image(np.dot(data, Rneg), dim=[1]) else: rnav3 = transform.transform_kspace_to_image(np.dot(data, Rpos), dim=[1]) scan += 1 # Phase correction delta = np.conj(rnav1 + rnav3) * rnav2 fdelta = np.tile(np.mean(delta, axis=0), [ncoils, 1]) corr = np.exp(sgn * 1j * np.angle(np.sqrt(fdelta))) for j in range(nky): acq = dset.read_acquisition(scan) slice = acq.idx.slice if slice != currslice: # end of this slice break ky = acq.idx.kspace_encode_step_1 data = coils.apply_prewhitening(acq.data, noise.preWMtx) if acq.isFlagSet(ismrmrd.ACQ_IS_REVERSE): rho = transform.transform_kspace_to_image(np.dot(data, Rneg), dim=[1]) H[slice, :, ky, :] = kfilty[ky] * np.conj(corr) * rho else: rho = transform.transform_kspace_to_image(np.dot(data, Rpos), dim=[1]) H[slice, :, ky, :] = kfilty[ky] * corr * rho scan += 1 # Close the data set dset.close() # Recon in along y H = transform.transform_kspace_to_image(H, dim=[2]) # Combine with SENSE weights epi_im = np.abs(np.squeeze(np.sum(H * unmix, axis=1))) return epi_im
#%% # Apply prewhitening kspace = coils.apply_prewhitening(kspace, dmtx) data = coils.apply_prewhitening(data, dmtx) #%% # Reconstruct aliased images alias_img = transform.transform_kspace_to_image(kspace, dim=(1, 2)) * np.sqrt(acc_factor) show.imshow(abs(alias_img)) #%% reload(sense) (unmix_sense, gmap_sense) = sense.calculate_sense_unmixing(acc_factor, csm) show.imshow(abs(gmap_sense), colorbar=True) recon_sense = np.squeeze(np.sum(alias_img * unmix_sense, 0)) show.imshow(abs(recon_sense), colorbar=True) #%% reload(grappa) # (unmix_grappa,gmap_grappa) = grappa.calculate_grappa_unmixing(data, acc_factor, data_mask=pat>1, csm=csm,kernel_size=(2,5)) (unmix_grappa, gmap_grappa) = grappa.calculate_grappa_unmixing(data, acc_factor, data_mask=pat > 1, kernel_size=(2, 5)) show.imshow(abs(gmap_grappa), colorbar=True) recon_grappa = np.squeeze(np.sum(alias_img * unmix_grappa, 0)) show.imshow(abs(recon_grappa), colorbar=True) #%% # Pseudo replica example reps = 255