def mmrchain( datain, # all input data in a dictionary scanner_params, # all scanner parameters in one dictionary # containing constants, transaxial and axial # LUTs. outpath='', # output path for results frames=['fluid', [0, 0]], # definition of time frames. mu_h=[], # hardware mu-map. mu_o=[], # object mu-map. tAffine=[], # affine transformations for the mu-map for # each time frame separately. itr=4, # number of OSEM iterations fwhm=0., # Gaussian Smoothing FWHM recmod=-1, # reconstruction mode: -1: undefined, chosen # automatically. 3: attenuation and scatter # correction, 1: attenuation correction # only, 0: no correction (randoms only). histo=[], # input histogram (from list-mode data); # if not given, it will be performed. trim=False, trim_scale=2, trim_interp=1, # interpolation for upsampling used in PVC trim_memlim=True, # reduced use of memory for machines # with limited memory (slow though) pvcroi=[], # ROI used for PVC. If undefined no PVC # is performed. psfkernel=[], pvcitr=5, fcomment='', # text comment used in the file name of # generated image files ret_sinos=False, # return prompt, scatter and randoms # sinograms for each reconstruction store_img=True, store_img_intrmd=False, store_itr=[], # store any reconstruction iteration in # the list. ignored if the list is empty. del_img_intrmd=False): logging.debug('#decompose all the scanner parameters and constants') Cnt = scanner_params['Cnt'] txLUT = scanner_params['txLUT'] axLUT = scanner_params['axLUT'] # ------------------------------------------------------------------------- logging.debug('# FRAMES') logging.debug('# check for the provided dynamic frames') if isinstance(frames, list): # Can be given in three ways: # * a 1D list (duration of each frame is listed) # * a more concise 2D list--repetition and duration lists in # each entry. Must start with the 'def' entry. # * a 2D list with fluid timings: must start with the string # 'fluid' or 'timings. a 2D list with consecutive lists # describing start and end of the time frame, [t0, t1]; # The number of time frames for this option is unlimited, # provided the t0 and t1 are within the acquisition times. if isinstance(frames[0], basestring) and (frames[0]=='fluid' or frames[0]=='timings') \ and all([isinstance(t,list) and len(t)==2 for t in frames[1:]]): logging.debug("# 2D starting with entry 'fluid' or 'timings'") t_frms = frames[1:] elif isinstance(frames[0], basestring) and frames[0]=='def' \ and all([isinstance(t,list) and len(t)==2 for t in frames[1:]]): logging.debug("# if 2D definitions, starting with entry 'def':") # get total time and list of all time frames dfrms = dynamic_timings(frames) t_frms = dfrms['timings'][1:] elif all([isinstance(t, integers) for t in frames]): logging.debug('# if 1D:') # get total time and list of all time frames dfrms = dynamic_timings(frames) t_frms = dfrms['timings'][1:] else: print 'e> osemdyn: frames definitions are not given in the correct list format: 1D [15,15,30,30,...] or 2D list [[2,15], [2,30], ...]' else: print 'e> osemdyn: provided dynamic frames definitions are not in either Python list or nympy array.' raise TypeError('Wrong data type for dynamic frames') logging.debug('# number of dynamic time frames->' + str(len(t_frms))) nfrm = len(t_frms) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- logging.debug('# create folders for results') if outpath == '': petdir = os.path.join(datain['corepath'], 'reconstructed') fmudir = os.path.join(datain['corepath'], 'mumap-obj') pvcdir = os.path.join(datain['corepath'], 'PRCL') else: petdir = os.path.join(outpath, 'PET') fmudir = os.path.join(outpath, 'mumap-obj') pvcdir = os.path.join(outpath, 'PRCL') logging.debug( '# folder for co-registered mu-maps (for motion compensation)') fmureg = os.path.join(fmudir, 'registered') logging.debug('# folder for affine transformation MR/CT->PET') petaff = os.path.join(petdir, 'faffine') logging.debug( '# folder for reconstructed images (dynamic or static depending on number of frames).' ) if nfrm > 1: petimg = os.path.join(petdir, 'multiple-frames') pvcdir = os.path.join(pvcdir, 'multiple-frames') elif nfrm == 1: petimg = os.path.join(petdir, 'single-frame') pvcdir = os.path.join(pvcdir, 'single-frame') else: print 'e> confused!' raise TypeError('Unrecognised time frames!') # create now the folder nimpa.create_dir(petimg) # create folder nimpa.create_dir(petdir) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- logging.debug('# MU-MAPS') logging.debug( '# get the mu-maps, if given; otherwise will use blank mu-maps.') if tAffine: muod = obtain_image(mu_o, imtype='object mu-map', verbose=Cnt['VERBOSE']) else: muod = obtain_image(mu_o, Cnt=Cnt, imtype='object mu-map') logging.debug('# hardware mu-map') muhd = obtain_image(mu_h, Cnt, imtype='hardware mu-map') logging.debug( '# choose the mode of reconstruction based on the provided (or not) mu-maps' ) if muod['exists'] and muhd['exists'] and recmod == -1: recmod = 3 elif (muod['exists'] or muhd['exists']) and recmod == -1: recmod = 1 print 'w> partial mu-map: scatter correction is switched off.' else: if recmod == -1: recmod = 0 print 'w> no mu-map provided: scatter and attenuation corrections are switched off.' # ------------------------------------------------------------------------- #import pdb; pdb.set_trace() # output dictionary output = {} output['recmod'] = recmod output['frames'] = t_frms output['#frames'] = nfrm logging.debug('pipe.mmrchain.output->') logging.debug(output) logging.debug( '# if affine transformation is given the baseline mu-map in NIfTI file or dictionary has to be given' ) if not tAffine: if Cnt['VERBOSE']: print 'i> using the provided mu-map the same way for all frames.' else: if len(tAffine) != nfrm: print 'e> the number of affine transformations in the list has to be the same as the number of dynamic frames!' raise IndexError('Inconsistent number of frames.') elif not isinstance(tAffine, list): print 'e> tAffine has to be a list of either 4x4 numpy arrays of affine transformations or a list of file path strings!' raise IndexError('Expecting a list.') elif not 'fim' in muod: print 'e> when tAffine is given, the object mu-map has to be provided either as a dictionary or NIfTI file!' raise NameError('No path to object mu-map.') logging.debug( '# check if all are file path strings to the existing files') if all([isinstance(t, basestring) for t in tAffine]): if all([os.path.isfile(t) for t in tAffine]): # the internal list of affine transformations faff_frms = tAffine if Cnt['VERBOSE']: print 'i> using provided paths to affine transformations for each dynamic frame.' else: print 'e> not all provided paths are valid!' raise IOError('Wrong paths.') # check if all are numpy arrays elif all([isinstance(t, (np.ndarray, np.generic)) for t in tAffine]): # create the folder for dynamic affine transformations nimpa.create_dir(petaff) faff_frms = [] for i in range(nfrm): fout = os.path.join(petaff, 'affine_frame(' + str(i) + ').txt') np.savetxt(fout, tAffine[i], fmt='%3.9f') faff_frms.append(fout) if Cnt['VERBOSE']: print 'i> using provided numpy arrays affine transformations for each dynamic frame.' else: raise StandardError( 'Affine transformations for each dynamic frame could not be established.' ) # ------------------------------------------------------------------------------------- logging.debug('# get ref image for mu-map resampling') # ------------------------------------------------------------------------------------- if 'fmuref' in muod: fmuref = muod['fmuref'] if Cnt['VERBOSE']: print 'i> reusing the reference mu-map from the object mu-map dictionary.' else: # create folder if doesn't exists nimpa.create_dir(fmudir) # ref file name fmuref = os.path.join(fmudir, 'muref.nii.gz') # ref affine B = image_affine(datain, Cnt, gantry_offset=False) # ref image (blank) im = np.zeros((Cnt['SO_IMZ'], Cnt['SO_IMY'], Cnt['SO_IMX']), dtype=np.float32) # store ref image nimpa.array2nii(im, B, fmuref) if Cnt['VERBOSE']: print 'i> generated a reference mu-map in', fmuref # ------------------------------------------------------------------------------------- output['fmuref'] = fmuref output['faffine'] = faff_frms logging.debug( '# output list of intermidiate file names for mu-maps and PET images (useful for dynamic imaging)' ) if tAffine: output['fmureg'] = [] if store_img_intrmd: output['fpeti'] = [] logging.debug('# dynamic images in one numpy array') dynim = np.zeros((nfrm, Cnt['SO_IMZ'], Cnt['SO_IMY'], Cnt['SO_IMY']), dtype=np.float32) logging.debug( '#if asked, output only scatter+randoms sinogram for each frame') if ret_sinos and itr > 1 and recmod > 2: dynrsn = np.zeros((nfrm, Cnt['NSN11'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) dynssn = np.zeros((nfrm, Cnt['NSN11'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) dynpsn = np.zeros((nfrm, Cnt['NSN11'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) # import pdb; pdb.set_trace() logging.debug('# starting frame index with reasonable prompt data') ifrmP = 0 logging.debug('# iterate over frame index') for ifrm in range(nfrm): # start time of a current (ifrm-th) dynamic frame t0 = int(t_frms[ifrm][0]) # end time of a current (ifrm-th) dynamic frame t1 = int(t_frms[ifrm][1]) # -------------- logging.debug( '# check if there is enough prompt data to do a reconstruction') # -------------- print 'i> dynamic frame times t0, t1:', t0, t1 if not histo: hst = mmrhist(datain, scanner_params, t0=t0, t1=t1) else: hst = histo print '' print 'i> using provided histogram' print '' if np.sum(hst['dhc']) > 0.99 * np.sum(hst['phc']): print '===============================================================================================' print 'w> the amount of random events is the greatest part of prompt events => omitting reconstruction' print '===============================================================================================' ifrmP = ifrm + 1 continue # -------------------- logging.debug( '# transform the mu-map if given the affine transformation for each frame' ) if tAffine: # create the folder for aligned (registered for motion compensation) mu-maps nimpa.create_dir(fmureg) # the converted nii image resample to the reference size fmu = os.path.join( fmureg, 'mumap_dyn_frm' + str(ifrm) + fcomment + '.nii.gz') logging.debug('# command for resampling') if os.path.isfile(Cnt['RESPATH']): cmd = [ Cnt['RESPATH'], '-ref', fmuref, '-flo', muod['fim'], '-trans', faff_frms[ifrm], '-res', fmu, '-pad', '0' ] if not Cnt['VERBOSE']: cmd.append('-voff') call(cmd) else: print 'e> path to the executable for resampling is incorrect!' raise IOError('Incorrect NiftyReg (resampling) executable.') # get the new mu-map from the just resampled file muodct = nimpa.getnii(fmu, output='all') muo = muodct['im'] A = muodct['affine'] muo[muo < 0] = 0 output['fmureg'].append(fmu) else: muo = muod['im'] #--------------------- logging.debug('# output image file name') if nfrm > 1: frmno = '_frm' + str(ifrm) else: frmno = '' logging.debug('# run OSEM reconstruction of a single time frame') recimg = mmrrec.osemone(datain, [muhd['im'], muo], hst, scanner_params, recmod=recmod, itr=itr, fwhm=fwhm, outpath=petimg, frmno=frmno, fcomment=fcomment + '_i', store_img=store_img_intrmd, store_itr=store_itr, ret_sinos=ret_sinos) logging.debug('# form dynamic numpy array') dynim[ifrm, :, :, :] = recimg.im if ret_sinos and itr > 1 and recmod > 2: dynpsn[ifrm, :, :, :] = hst['psino'] dynssn[ifrm, :, :, :] = recimg.ssn dynrsn[ifrm, :, :, :] = recimg.rsn if store_img_intrmd: output['fpeti'].append(recimg.fpet) if nfrm == 1: output['tuple'] = recimg output['im'] = np.squeeze(dynim) if ret_sinos and itr > 1 and recmod > 2: output['sinos'] = {'psino': dynpsn, 'ssino': dynssn, 'rsino': dynrsn} # ---------------------------------------------------------------------- logging.debug('# trim the PET image') logging.debug('# images have to be stored for PVC') if pvcroi: store_img_intrmd = True if trim: logging.debug('# trim; create file name') if 'lm_dcm' in datain: fnm = os.path.basename(datain['lm_dcm'])[:20] elif 'lm_ima' in datain: fnm = os.path.basename(datain['lm_ima'])[:20] logging.debug('# trim PET and upsample') petu = nimpa.trimim(dynim, affine=image_affine(datain, Cnt), scale=trim_scale, int_order=trim_interp, outpath=petimg, fname=fnm, fcomment=fcomment, store_img_intrmd=store_img_intrmd, memlim=trim_memlim, verbose=Cnt['VERBOSE']) output.update({ 'trimmed': { 'im': petu['im'], 'fpet': petu['fimi'], 'affine': petu['affine'] } }) # ---------------------------------------------------------------------- # ---------------------------------------------------------------------- logging.debug('#run PVC if requested and required input given') if pvcroi: if not os.path.isfile(datain['T1lbl']): print 'e> no label image from T1 parcellations and/or ROI definitions!' raise StandardError('No ROIs') else: # get the PSF kernel for PVC if not psfkernel: psfkernel = nimpa.psf_measured(scanner='mmr', scale=trim_scale) else: if isinstance( psfkernel, (np.ndarray, np.generic)) and psfkernel.shape != (3, 17): print 'e> the PSF kernel has to be an numpy array with the shape of (3, 17)!' raise IndexError('PSF: wrong shape or not a matrix') logging.debug('# perform PVC for each time frame') froi2 = [] fpvc = [] dynpvc = np.zeros(petu['im'].shape, dtype=np.float32) for i in range(ifrmP, nfrm): # transform the parcelations (ROIs) if given the affine transformation for each frame if not tAffine: print 'w> affine transformation are not provided: will generate for the time frame.' faffpvc = '' #raise StandardError('No affine transformation') else: faffpvc = faff_frms[i] # chose file name of individual PVC images if nfrm > 1: fcomment_pvc = '_frm' + str(i) + fcomment else: fcomment_pvc = fcomment #============================ logging.debug('# perform PVC') petpvc_dic = nimpa.pvc_iyang(petu['fimi'][i], datain, Cnt, pvcroi, psfkernel, itr=pvcitr, faff=faffpvc, fcomment=fcomment_pvc, outpath=pvcdir, store_img=store_img_intrmd) #============================ if nfrm > 1: dynpvc[i, :, :, :] = petpvc_dic['im'] else: dynpvc = petpvc_dic['im'] froi2.append(petpvc_dic['froi']) fpvc.append(petpvc_dic['fpet']) logging.debug('# update output dictionary') output.update({'impvc': dynpvc, 'froi': froi2, 'fpvc': fpvc}) # ---------------------------------------------------------------------- if store_img: logging.debug('# description for saving NIFTI image') logging.debug( '# attenuation number: if only bed present then it is 0.5') attnum = (1 * muhd['exists'] + 1 * muod['exists']) / 2. descrip = 'alg=osem' \ +';att='+str(attnum*(recmod>0)) \ +';sct='+str(1*(recmod>1)) \ +';spn='+str(Cnt['SPN']) \ +';sub=14' \ +';itr='+str(itr) \ +';fwhm='+str(fwhm) \ +';nfrm='+str(nfrm) # squeeze the not needed dimensions dynim = np.squeeze(dynim) logging.debug( '# NIfTI file name for the full PET image (single or multiple frame)' ) logging.debug('# save the image to NIfTI file') if nfrm == 1: t0 = hst['t0'] t1 = hst['t1'] if t1 == t0: t0 = 0 t1 = hst['dur'] fpet = os.path.join( petimg, os.path.basename(recimg.fpet)[:8] + '_itr-' + str(itr) + '_t-' + str(t0) + '-' + str(t1) + 'sec') fpeto = fpet + fcomment + '.nii.gz' nimpa.prc.array2nii(dynim[::-1, ::-1, :], recimg.affine, fpeto, descrip=descrip) else: fpet = os.path.join( petimg, os.path.basename(recimg.fpet)[:8] + '_itr-' + str(itr) + '_nfrm-' + str(nfrm)) fpeto = fpet + fcomment + '.nii.gz' nimpa.prc.array2nii(dynim[:, ::-1, ::-1, :], recimg.affine, fpeto, descrip=descrip) logging.debug('# get output file names for trimmed/PVC images') if trim: logging.debug('# trim; folder for trimmed and dynamic') pettrim = os.path.join(petimg, 'trimmed') # make folder nimpa.create_dir(pettrim) # trimming scale added to NIfTI descritoption descrip_trim = descrip + ';trim_scale=' + str(trim_scale) # file name for saving the trimmed image fpetu = os.path.join( pettrim, os.path.basename(fpet) + '_trimmed-upsampled-scale-' + str(trim_scale)) # in case of PVC if pvcroi: logging.debug( '# itertive Yang (iY) added to NIfTI descritoption') descrip_pvc = descrip_trim + ';pvc=iY' # file name for saving the PVC NIfTI image fpvc = fpetu + '_PVC' + fcomment + '.nii.gz' output['trimmed']['fpvc'] = fpvc # update the trimmed image file name fpetu += fcomment + '.nii.gz' logging.debug( '# trim; store the file name in the output dictionary') output['trimmed']['fpet'] = fpetu output['fpet'] = fpeto logging.debug('# save images') if nfrm == 1: if trim: nimpa.prc.array2nii(petu['im'][::-1, ::-1, :], petu['affine'], fpetu, descrip=descrip_trim) if pvcroi: nimpa.prc.array2nii(dynpvc[::-1, ::-1, :], petu['affine'], fpvc, descrip=descrip_pvc) elif nfrm > 1: if trim: nimpa.prc.array2nii(petu['im'][:, ::-1, ::-1, :], petu['affine'], fpetu, descrip=descrip_trim) if pvcroi: nimpa.prc.array2nii(dynpvc[:, ::-1, ::-1, :], petu['affine'], fpvc, descrip=descrip_pvc) if del_img_intrmd: if pvcroi: for fi in fpvc: os.remove(fi) if trim: for fi in petu['fimi']: os.remove(fi) return output
def vsm( datain, mumaps, em, hst, rsinos, scanner_params, prcnt_scl=0.1, emmsk=False, return_uninterp=False, return_ssrb=False, return_mask=False, ): ''' Voxel-driven scatter modelling (VSM). Obtain a scatter sinogram using the mu-maps (hardware and object mu-maps) an estimate of emission image, the prompt measured sinogram, an estimate of the randoms sinogram and a normalisation sinogram. Input: - datain: Contains the data used for scatter-specific detector normalisation. May also include the non-corrected emission image used for masking, when requested. - mumaps: A tuple of hardware and object mu-maps (in this order). - em: An estimate of the emission image. - hst: Dictionary containing the histogrammed measured data into sinograms. - rsinos: Randoms sinogram (3D). Needed for proper scaling of scatter to the prompt data. - scanner_params: Scanner specific parameters. - prcnt_scl: Ratio of the maximum scatter intensities below which the scatter is not used for fitting it to the tails of prompt data. Default is 10%. - emmsk: When 'True' it will use uncorrected emission image for masking the sources (voxels) of photons to be used in the scatter modelling. ''' log = logging.getLogger(__name__) muh, muo = mumaps #-constants, transaxial and axial LUTs are extracted Cnt = scanner_params['Cnt'] txLUT = scanner_params['txLUT'] axLUT = scanner_params['axLUT'] if emmsk and not os.path.isfile(datain['em_nocrr']): log.info( 'reconstruction of emission data without scatter and attenuation correction for mask generation' ) recnac = mmrrec.osemone(datain, mumaps, hst, scanner_params, recmod=0, itr=3, fwhm=2.0, store_img=True) datain['em_nocrr'] = recnac.fpet #-get the normalisation components nrmcmp, nhdr = mmrnorm.get_components(datain, Cnt) #-smooth for defining the sino scatter only regions mu_sctonly = ndi.filters.gaussian_filter(mmrimg.convert2dev(muo, Cnt), fwhm2sig(0.42, Cnt), mode='mirror') if Cnt['SPN'] == 1: snno = Cnt['NSN1'] snno_ = Cnt['NSN64'] ssrlut = axLUT['sn1_ssrb'] saxnrm = nrmcmp['sax_f1'] elif Cnt['SPN'] == 11: snno = Cnt['NSN11'] snno_ = snno ssrlut = axLUT['sn11_ssrb'] saxnrm = nrmcmp['sax_f11'] #LUTs for scatter sctLUT = get_sctLUT(Cnt) #-smooth before down-sampling mu-map and emission image muim = ndi.filters.gaussian_filter(muo + muh, fwhm2sig(0.42, Cnt), mode='mirror') muim = ndi.interpolation.zoom(muim, Cnt['SCTSCLMU'], order=3) #(0.499, 0.5, 0.5) emim = ndi.filters.gaussian_filter(em, fwhm2sig(0.42, Cnt), mode='mirror') emim = ndi.interpolation.zoom(emim, Cnt['SCTSCLEM'], order=3) #(0.34, 0.33, 0.33) #emim = ndi.interpolation.zoom( emim, (0.499, 0.5, 0.5), order=3 ) #-smooth the mu-map for mask creation. the mask contains voxels for which attenuation ray LUT is found. smomu = ndi.filters.gaussian_filter(muim, fwhm2sig(0.84, Cnt), mode='mirror') mumsk = np.int8(smomu > 0.003) #CORE SCATTER ESTIMATION NSCRS, NSRNG = 64, 8 sctout = { 'xsxu': np.zeros((NSCRS, NSCRS / 2), dtype=np.int8), #one when xs>xu, otherwise zero 'bin_indx': np.zeros((NSCRS, NSCRS / 2), dtype=np.int32), 'sct_val': np.zeros((Cnt['TOFBINN'], NSRNG, NSCRS, NSRNG, NSCRS / 2), dtype=np.float32), 'sct_3d': np.zeros((Cnt['TOFBINN'], snno_, NSCRS, NSCRS / 2), dtype=np.float32) } #<<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>> petsct.scatter(sctout, muim, mumsk, emim, sctLUT, txLUT, axLUT, Cnt) #<<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>> sct3d = sctout['sct_3d'] sctind = sctout['bin_indx'] log.debug('total scatter sum:%r' % np.sum(sct3d)) if np.sum(sct3d) < 1e-04: sss = np.zeros((snno, Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) amsksn = np.zeros((snno, Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) sssr = np.zeros((Cnt['NSEG0'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) return sss, sssr, amsksn #> get SSR for randoms from span-1 or span-11 rssr = np.zeros((Cnt['NSEG0'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) for i in range(snno): rssr[ssrlut[i], :, :] += rsinos[i, :, :] #ATTENUATION FRACTIONS for scatter only regions, and NORMALISATION for all SCATTER #<<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>> currentspan = Cnt['SPN'] Cnt['SPN'] = 1 atto = np.zeros((txLUT['Naw'], Cnt['NSN1']), dtype=np.float32) petprj.fprj(atto, mu_sctonly, txLUT, axLUT, np.array([-1], dtype=np.int32), Cnt, 1) atto = mmraux.putgaps(atto, txLUT, Cnt) #-------------------------------------------------------------- #get norm components setting the geometry and axial to ones as they are accounted for differently nrmcmp['geo'][:] = 1 nrmcmp['axe1'][:] = 1 #get sino with no gaps nrmg = np.zeros((txLUT['Naw'], Cnt['NSN1']), dtype=np.float32) mmr_auxe.norm(nrmg, nrmcmp, hst['buckets'], axLUT, txLUT['aw2ali'], Cnt) nrm = mmraux.putgaps(nrmg, txLUT, Cnt) #-------------------------------------------------------------- #get attenuation + norm in (span-11) and SSR attossr = np.zeros((Cnt['NSEG0'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) nrmsssr = np.zeros((Cnt['NSEG0'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) for i in range(Cnt['NSN1']): si = axLUT['sn1_ssrb'][i] attossr[si, :, :] += atto[i, :, :] / float(axLUT['sn1_ssrno'][si]) nrmsssr[si, :, :] += nrm[i, :, :] / float(axLUT['sn1_ssrno'][si]) if currentspan == 11: Cnt['SPN'] = 11 nrmg = np.zeros((txLUT['Naw'], snno), dtype=np.float32) mmr_auxe.norm(nrmg, nrmcmp, hst['buckets'], axLUT, txLUT['aw2ali'], Cnt) nrm = mmraux.putgaps(nrmg, txLUT, Cnt) #-------------------------------------------------------------- #get the mask for the object from uncorrected emission image if emmsk and os.path.isfile(datain['em_nocrr']): nim = nib.load(datain['em_nocrr']) A = nim.get_sform() eim = np.float32(nim.get_data()) eim = eim[:, ::-1, ::-1] eim = np.transpose(eim, (2, 1, 0)) em_sctonly = ndi.filters.gaussian_filter(eim, fwhm2sig(.6, Cnt), mode='mirror') msk = np.float32(em_sctonly > 0.07 * np.max(em_sctonly)) msk = ndi.filters.gaussian_filter(msk, fwhm2sig(.6, Cnt), mode='mirror') msk = np.float32(msk > 0.01) msksn = mmrprj.frwd_prj(msk, txLUT, axLUT, Cnt) mssr = mmraux.sino2ssr(msksn, axLUT, Cnt) mssr = mssr > 0 else: mssr = np.zeros((Cnt['NSEG0'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.bool) #<<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>><<+>> #-------------------------------------------------------------------------------------------- # get scatter sinos for TOF or non-TOF if Cnt['TOFBINN'] > 1: ssn = np.zeros((Cnt['TOFBINN'], snno, Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float64) sssr = np.zeros( (Cnt['TOFBINN'], Cnt['NSEG0'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) tmp2d = np.zeros((Cnt['NSANGLES'] * Cnt['NSBINS']), dtype=np.float64) log.info('interpolate each scatter sino...') for k in range(Cnt['TOFBINN']): log.info('doing TOF bin k = %d' % k) for i in range(snno): tmp2d[:] = 0 for ti in range(len(sctind)): tmp2d[sctind[ti]] += sct3d[k, i, ti] #interpolate estimated scatter ssn[k, i, :, :] = get_sctinterp( np.reshape(tmp2d, (Cnt['NSANGLES'], Cnt['NSBINS'])), sctind, Cnt) sssr[k, ssrlut[i], :, :] += ssn[k, i, :, :] log.info('TOF bin #%d' % k) elif Cnt['TOFBINN'] == 1: ssn = np.zeros((snno, Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) sssr = np.zeros((Cnt['NSEG0'], Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) tmp2d = np.zeros((Cnt['NSANGLES'] * Cnt['NSBINS']), dtype=np.float32) log.info('scatter sinogram interpolation...') for i in trange(snno, desc="interpolating", unit="sinogram", leave=log.getEffectiveLevel() < logging.INFO): tmp2d[:] = 0 for ti in range(len(sctind)): tmp2d[sctind[ti]] += sct3d[0, i, ti] #interpolate estimated scatter ssn[i, :, :] = get_sctinterp( np.reshape(tmp2d, (Cnt['NSANGLES'], Cnt['NSBINS'])), sctind, Cnt) sssr[ssrlut[i], :, :] += ssn[i, :, :] #-------------------------------------------------------------------------------------------- #=== scale scatter for ssr and non-TOF=== #mask rmsk = (txLUT['msino'] > 0).T rmsk.shape = (1, Cnt['NSANGLES'], Cnt['NSBINS']) rmsk = np.repeat(rmsk, Cnt['NSEG0'], axis=0) amsksn = np.logical_and(attossr >= 0.999, rmsk) * ~mssr #scaling factors for ssr scl_ssr = np.zeros((Cnt['NSEG0']), dtype=np.float32) for sni in range(Cnt['NSEG0']): # region of choice for scaling thrshld = prcnt_scl * np.max(sssr[sni, :, :]) amsksn[sni, :, :] *= (sssr[sni, :, :] > thrshld) amsk = amsksn[sni, :, :] #normalised estimated scatter mssn = sssr[sni, :, :] * nrmsssr[sni, :, :] mssn[np.invert(amsk)] = 0 #vectorised masked sino vssn = mssn[amsk] vpsn = hst['pssr'][sni, amsk] - rssr[sni, amsk] scl_ssr[sni] = np.sum(vpsn) / np.sum(mssn) #ssr output sssr[sni, :, :] *= nrmsssr[sni, :, :] * scl_ssr[sni] #=== scale scatter for the proper sino === sss = np.zeros((snno, Cnt['NSANGLES'], Cnt['NSBINS']), dtype=np.float32) for i in range(snno): sss[i, :, :] = ssn[i, :, :] * scl_ssr[ssrlut[i]] * saxnrm[i] * nrm[ i, :, :] out = {} if return_uninterp: out['uninterp'] = sct3d out['indexes'] = sctind if return_ssrb: out['ssrb'] = sssr if return_mask: out['mask'] = amsksn if not out: return sss else: out['sino'] = sss return out
def align_mumap( datain, scanner_params=None, outpath='', reg_tool='niftyreg', use_stored=False, hst=None, t0=0, t1=0, itr=2, faff='', fpet='', fcomment='', store=False, store_npy=False, petopt='ac', musrc='ute', # another option is pct for mu-map source ute_name='UTE2', del_auxilary=True, verbose=False, ): ''' Align the a pCT or MR-derived mu-map to a PET image reconstructed to chosen specifications (e.g., with/without attenuation and scatter corrections) use_sotred only works if hst or t0/t1 given but not when faff. ''' if scanner_params is None: scanner_params = {} # > output folder if outpath == '': opth = os.path.join(datain['corepath'], 'mumap-obj') else: opth = os.path.join(outpath, 'mumap-obj') # > create the folder, if not existent nimpa.create_dir(opth) # > get the timing of PET if affine not given if faff == '' and hst is not None and isinstance(hst, dict) and 't0' in hst: t0 = hst['t0'] t1 = hst['t1'] # > file name for the output mu-map fnm = 'mumap-' + musrc.upper() # > output dictionary mu_dct = {} # --------------------------------------------------------------------------- # > used stored if requested if use_stored: fmu_stored = fnm + '-aligned-to_t'\ + str(t0)+'-'+str(t1)+'_'+petopt.upper()\ + fcomment fmupath = os.path.join(opth, fmu_stored + '.nii.gz') if os.path.isfile(fmupath): mudct_stored = nimpa.getnii(fmupath, output='all') # > create output dictionary mu_dct['im'] = mudct_stored['im'] mu_dct['affine'] = mudct_stored['affine'] # pu_dct['faff'] = faff return mu_dct # --------------------------------------------------------------------------- # > tmp folder for not aligned mu-maps tmpdir = os.path.join(opth, 'tmp') nimpa.create_dir(tmpdir) # > three ways of passing scanner constants <Cnt> are here decoded if 'Cnt' in scanner_params: Cnt = scanner_params['Cnt'] elif 'SO_IMZ' in scanner_params: Cnt = scanner_params else: Cnt = rs.get_mmr_constants() # > if affine not provided histogram the LM data for recon and registration if not os.path.isfile(faff): from niftypet.nipet.prj import mmrrec # -histogram the list data if needed if hst is None: from niftypet.nipet import mmrhist if 'txLUT' in scanner_params: hst = mmrhist(datain, scanner_params, t0=t0, t1=t1) else: raise ValueError('Full scanner are parameters not provided\ but are required for histogramming.') # ======================================================== # -get hardware mu-map if 'hmumap' in datain and os.path.isfile(datain['hmumap']): muh = np.load(datain['hmumap'], allow_pickle=True)["hmu"] (log.info if verbose else log.debug)('loaded hardware mu-map from file:\n{}'.format( datain['hmumap'])) elif outpath != '': hmupath = os.path.join(outpath, "mumap-hdw", "hmumap.npz") if os.path.isfile(hmupath): muh = np.load(hmupath, allow_pickle=True)["hmu"] datain["hmumap"] = hmupath else: raise IOError('Invalid path to the hardware mu-map') else: log.error('the hardware mu-map is required first.') raise IOError('Could not find the hardware mu-map!') # ======================================================== # -check if T1w image is available if not {'MRT1W#', 'T1nii', 'T1bc', 'T1N4'}.intersection(datain): log.error('no MR T1w images required for co-registration!') raise IOError('T1w image could not be obtained!') # ======================================================== # -if the affine is not given, # -it will be generated by reconstructing PET image, with some or no corrections if not os.path.isfile(faff): # first recon pet to get the T1 aligned to it if petopt == 'qnt': # --------------------------------------------- # OPTION 1 (quantitative recon with all corrections using MR-based mu-map) # get UTE object mu-map (may not be in register with the PET data) mudic = obj_mumap(datain, Cnt, outpath=tmpdir, del_auxilary=del_auxilary) muo = mudic['im'] # reconstruct PET image with UTE mu-map to which co-register T1w recout = mmrrec.osemone(datain, [muh, muo], hst, scanner_params, recmod=3, itr=itr, fwhm=0., fcomment=fcomment + '_QNT-UTE', outpath=os.path.join(outpath, 'PET', 'positioning'), store_img=True) elif petopt == 'nac': # --------------------------------------------- # OPTION 2 (recon without any corrections for scatter and attenuation) # reconstruct PET image with UTE mu-map to which co-register T1w muo = np.zeros(muh.shape, dtype=muh.dtype) recout = mmrrec.osemone(datain, [muh, muo], hst, scanner_params, recmod=1, itr=itr, fwhm=0., fcomment=fcomment + '_NAC', outpath=os.path.join(outpath, 'PET', 'positioning'), store_img=True) elif petopt == 'ac': # --------------------------------------------- # OPTION 3 (recon with attenuation correction only but no scatter) # reconstruct PET image with UTE mu-map to which co-register T1w mudic = obj_mumap(datain, Cnt, outpath=tmpdir, del_auxilary=del_auxilary) muo = mudic['im'] recout = mmrrec.osemone(datain, [muh, muo], hst, scanner_params, recmod=1, itr=itr, fwhm=0., fcomment=fcomment + '_AC-UTE', outpath=os.path.join(outpath, 'PET', 'positioning'), store_img=True) fpet = recout.fpet mu_dct['fpet'] = fpet # ------------------------------ if musrc == 'ute' and ute_name in datain and os.path.exists(datain[ute_name]): # change to NIfTI if the UTE sequence is in DICOM files (folder) if os.path.isdir(datain[ute_name]): fnew = os.path.basename(datain[ute_name]) run([Cnt['DCM2NIIX'], '-f', fnew, datain[ute_name]]) fute = glob.glob(os.path.join(datain[ute_name], fnew + '*nii*'))[0] elif os.path.isfile(datain[ute_name]): fute = datain[ute_name] # get the affine transformation if reg_tool == 'spm': regdct = nimpa.coreg_spm(fpet, fute, outpath=os.path.join(outpath, 'PET', 'positioning')) elif reg_tool == 'niftyreg': regdct = nimpa.affine_niftyreg( fpet, fute, outpath=os.path.join(outpath, 'PET', 'positioning'), executable=Cnt['REGPATH'], omp=multiprocessing.cpu_count() / 2, # pcomment=fcomment, rigOnly=True, affDirect=False, maxit=5, speed=True, pi=50, pv=50, smof=0, smor=0, rmsk=True, fmsk=True, rfwhm=15., # pillilitres rthrsh=0.05, ffwhm=15., # pillilitres fthrsh=0.05, verbose=verbose) else: raise ValueError('unknown registration tool requested') faff_mrpet = regdct['faff'] elif musrc == 'pct': ft1w = nimpa.pick_t1w(datain) if reg_tool == 'spm': regdct = nimpa.coreg_spm(fpet, ft1w, outpath=os.path.join(outpath, 'PET', 'positioning')) elif reg_tool == 'niftyreg': regdct = nimpa.affine_niftyreg( fpet, ft1w, outpath=os.path.join(outpath, 'PET', 'positioning'), executable=Cnt['REGPATH'], omp=multiprocessing.cpu_count() / 2, rigOnly=True, affDirect=False, maxit=5, speed=True, pi=50, pv=50, smof=0, smor=0, rmsk=True, fmsk=True, rfwhm=15., # pillilitres rthrsh=0.05, ffwhm=15., # pillilitres fthrsh=0.05, verbose=verbose) else: raise ValueError('unknown registration tool requested') faff_mrpet = regdct['faff'] else: raise IOError('Floating MR image not provided or is invalid.') else: faff_mrpet = faff regdct = {} if not os.path.isfile(fpet): raise IOError('e> the reference PET should be supplied with the affine.') # > output file name for the aligned mu-maps if musrc == 'pct': # > convert to mu-values before resampling to avoid artefacts with negative values nii = nib.load(datain['pCT']) img = nii.get_fdata(dtype=np.float32) img_mu = hu2mu(img) nii_mu = nib.Nifti1Image(img_mu, nii.affine) fflo = os.path.join(tmpdir, 'pct2mu-not-aligned.nii.gz') nib.save(nii_mu, fflo) freg = os.path.join(opth, 'pct2mu-aligned-' + fcomment + '.nii.gz') elif musrc == 'ute': freg = os.path.join(opth, 'UTE-res-tmp' + fcomment + '.nii.gz') if 'UTE' not in datain: fnii = 'converted-from-DICOM_' tstmp = nimpa.time_stamp(simple_ascii=True) # convert the DICOM mu-map images to nii if 'mumapDCM' not in datain: raise IOError('DICOM with the UTE mu-map are not given.') run([Cnt['DCM2NIIX'], '-f', fnii + tstmp, '-o', opth, datain['mumapDCM']]) # piles for the T1w, pick one: fflo = glob.glob(os.path.join(opth, '*' + fnii + tstmp + '*.nii*'))[0] else: if os.path.isfile(datain['UTE']): fflo = datain['UTE'] else: raise IOError('The provided NIfTI UTE path is not valid.') # > call the resampling routine to get the pCT/UTE in place if reg_tool == "spm": nimpa.resample_spm(fpet, fflo, faff_mrpet, fimout=freg, del_ref_uncmpr=True, del_flo_uncmpr=True, del_out_uncmpr=True) else: nimpa.resample_niftyreg(fpet, fflo, faff_mrpet, fimout=freg, executable=Cnt['RESPATH'], verbose=verbose) # -get the NIfTI of registered image nim = nib.load(freg) A = nim.affine imreg = nim.get_fdata(dtype=np.float32) imreg = imreg[:, ::-1, ::-1] imreg = np.transpose(imreg, (2, 1, 0)) # -convert to mu-values; sort out the file name too. if musrc == 'pct': mu = imreg elif musrc == 'ute': mu = np.float32(imreg) / 1e4 # -remove the converted file from DICOMs os.remove(fflo) else: raise NameError('Confused o_O') # > get rid of negatives and nans mu[mu < 0] = 0 mu[np.isnan(mu)] = 0 # > return image dictionary with the image itself and other parameters mu_dct['im'] = mu mu_dct['affine'] = A mu_dct['faff'] = faff_mrpet if store or store_npy: nimpa.create_dir(opth) if faff == '': fname = fnm + '-aligned-to_t'\ + str(t0)+'-'+str(t1)+'_'+petopt.upper()\ + fcomment else: fname = fnm + '-aligned-to-given-affine' + fcomment if store_npy: fnp = os.path.join(opth, fname + ".npz") np.savez(fnp, mu=mu, A=A) if store: # > NIfTI fmu = os.path.join(opth, fname + '.nii.gz') nimpa.array2nii(mu[::-1, ::-1, :], A, fmu) mu_dct['fim'] = fmu if del_auxilary: os.remove(freg) if musrc == 'ute' and not os.path.isfile(faff): os.remove(fute) shutil.rmtree(tmpdir) return mu_dct
def pct_mumap(datain, scanner_params, hst=None, t0=0, t1=0, itr=2, petopt='ac', faff='', fpet='', fcomment='', outpath='', store_npy=False, store=False, verbose=False): ''' GET THE MU-MAP from pCT IMAGE (which is in T1w space) * the mu-map will be registered to PET which will be reconstructed for time frame t0-t1 * it f0 and t1 are not given the whole LM dataset will be reconstructed * the reconstructed PET can be attenuation and scatter corrected or NOT using petopt ''' if hst is None: hst = [] # constants, transaxial and axial LUTs are extracted Cnt = scanner_params['Cnt'] if not os.path.isfile(faff): from niftypet.nipet.prj import mmrrec # histogram the list data if needed if not hst: from niftypet.nipet.lm import mmrhist hst = mmrhist(datain, scanner_params, t0=t0, t1=t1) # get hardware mu-map if datain.get("hmumap", "").endswith(".npz") and os.path.isfile(datain["hmumap"]): muh = np.load(datain["hmumap"], allow_pickle=True)["hmu"] (log.info if verbose else log.debug)('loaded hardware mu-map from file:\n{}'.format( datain['hmumap'])) elif outpath: hmupath = os.path.join(outpath, "mumap-hdw", "hmumap.npz") if os.path.isfile(hmupath): muh = np.load(hmupath, allow_pickle=True)["hmu"] datain['hmumap'] = hmupath else: raise IOError('Invalid path to the hardware mu-map') else: log.error('The hardware mu-map is required first.') raise IOError('Could not find the hardware mu-map!') if not {'MRT1W#', 'T1nii', 'T1bc'}.intersection(datain): log.error('no MR T1w images required for co-registration!') raise IOError('Missing MR data') # ---------------------------------- # output dictionary mu_dct = {} if not os.path.isfile(faff): # first recon pet to get the T1 aligned to it if petopt == 'qnt': # --------------------------------------------- # OPTION 1 (quantitative recon with all corrections using MR-based mu-map) # get UTE object mu-map (may not be in register with the PET data) mudic = obj_mumap(datain, Cnt) muo = mudic['im'] # reconstruct PET image with UTE mu-map to which co-register T1w recout = mmrrec.osemone(datain, [muh, muo], hst, scanner_params, recmod=3, itr=itr, fwhm=0., fcomment=fcomment + '_qntUTE', outpath=os.path.join(outpath, 'PET', 'positioning'), store_img=True) elif petopt == 'nac': # --------------------------------------------- # OPTION 2 (recon without any corrections for scatter and attenuation) # reconstruct PET image with UTE mu-map to which co-register T1w muo = np.zeros(muh.shape, dtype=muh.dtype) recout = mmrrec.osemone(datain, [muh, muo], hst, scanner_params, recmod=1, itr=itr, fwhm=0., fcomment=fcomment + '_NAC', outpath=os.path.join(outpath, 'PET', 'positioning'), store_img=True) elif petopt == 'ac': # --------------------------------------------- # OPTION 3 (recon with attenuation correction only but no scatter) # reconstruct PET image with UTE mu-map to which co-register T1w mudic = obj_mumap(datain, Cnt, outpath=outpath) muo = mudic['im'] recout = mmrrec.osemone(datain, [muh, muo], hst, scanner_params, recmod=1, itr=itr, fwhm=0., fcomment=fcomment + '_AC', outpath=os.path.join(outpath, 'PET', 'positioning'), store_img=True) fpet = recout.fpet mu_dct['fpet'] = fpet # ------------------------------ # get the affine transformation ft1w = nimpa.pick_t1w(datain) try: regdct = nimpa.coreg_spm(fpet, ft1w, outpath=os.path.join(outpath, 'PET', 'positioning')) except Exception: regdct = nimpa.affine_niftyreg( fpet, ft1w, outpath=os.path.join(outpath, 'PET', 'positioning'), # pcomment=fcomment, executable=Cnt['REGPATH'], omp=multiprocessing.cpu_count() / 2, rigOnly=True, affDirect=False, maxit=5, speed=True, pi=50, pv=50, smof=0, smor=0, rmsk=True, fmsk=True, rfwhm=15., # pillilitres rthrsh=0.05, ffwhm=15., # pillilitres fthrsh=0.05, verbose=verbose) faff = regdct['faff'] # ------------------------------ # pCT file name if outpath == '': pctdir = os.path.dirname(datain['pCT']) else: pctdir = os.path.join(outpath, 'mumap-obj') mmraux.create_dir(pctdir) fpct = os.path.join(pctdir, 'pCT_r_tmp' + fcomment + '.nii.gz') # > call the resampling routine to get the pCT in place if os.path.isfile(Cnt['RESPATH']): cmd = [ Cnt['RESPATH'], '-ref', fpet, '-flo', datain['pCT'], '-trans', faff, '-res', fpct, '-pad', '0'] if log.getEffectiveLevel() > logging.INFO: cmd.append('-voff') run(cmd) else: log.error('path to resampling executable is incorrect!') raise IOError('Incorrect path to executable!') # get the NIfTI of the pCT nim = nib.load(fpct) A = nim.get_sform() pct = nim.get_fdata(dtype=np.float32) pct = pct[:, ::-1, ::-1] pct = np.transpose(pct, (2, 1, 0)) # convert the HU units to mu-values mu = hu2mu(pct) # get rid of negatives mu[mu < 0] = 0 # return image dictionary with the image itself and other parameters mu_dct['im'] = mu mu_dct['affine'] = A mu_dct['faff'] = faff if store: # now save to numpy array and NIfTI in this folder if outpath == '': pctumapdir = os.path.join(datain['corepath'], 'mumap-obj') else: pctumapdir = os.path.join(outpath, 'mumap-obj') mmraux.create_dir(pctumapdir) # > Numpy if store_npy: fnp = os.path.join(pctumapdir, "mumap-pCT.npz") np.savez(fnp, mu=mu, A=A) # > NIfTI fmu = os.path.join(pctumapdir, 'mumap-pCT' + fcomment + '.nii.gz') nimpa.array2nii(mu[::-1, ::-1, :], A, fmu) mu_dct['fim'] = fmu datain['mumapCT'] = fmu return mu_dct
def rmumaps(datain, Cnt, t0=0, t1=0, use_stored=False): ''' get the mu-maps for hardware and object and trim it axially for reduced rings case ''' from niftypet.nipet.lm import mmrhist from niftypet.nipet.prj import mmrrec fcomment = '(R)' # get hardware mu-map if os.path.isfile(datain['hmumap']) and use_stored: muh = np.load(datain["hmumap"], allow_pickle=True)["hmu"] log.info('loaded hardware mu-map from file:\n{}'.format(datain['hmumap'])) else: hmudic = hdw_mumap(datain, [1, 2, 4], Cnt) muh = hmudic['im'] # get pCT mu-map if stored in numpy file and then exit, otherwise do all the processing if os.path.isfile(datain['mumapCT']) and use_stored: mup = np.load(datain["mumapCT"], allow_pickle=True)["mu"] muh = muh[2 * Cnt['RNG_STRT']:2 * Cnt['RNG_END'], :, :] mup = mup[2 * Cnt['RNG_STRT']:2 * Cnt['RNG_END'], :, :] return [muh, mup] # get UTE object mu-map (may be not in register with the PET data) if os.path.isfile(datain['mumapUTE']) and use_stored: muo, _ = np.load(datain['mumapUTE'], allow_pickle=True) else: mudic = obj_mumap(datain, Cnt, store=True) muo = mudic['im'] if os.path.isfile(datain['pCT']): # reconstruct PET image with default settings to be used to alight pCT mu-map params = mmraux.get_mmrparams() Cnt_ = params['Cnt'] txLUT_ = params['txLUT'] axLUT_ = params['axLUT'] # histogram for reconstruction with UTE mu-map hst = mmrhist.hist(datain, txLUT_, axLUT_, Cnt_, t0=t0, t1=t1) # reconstruct PET image with UTE mu-map to which co-register T1w recute = mmrrec.osemone(datain, [muh, muo], hst, params, recmod=3, itr=4, fwhm=0., store_img=True, fcomment=fcomment + '_QNT-UTE') # --- MR T1w if os.path.isfile(datain['T1nii']): ft1w = datain['T1nii'] elif os.path.isfile(datain['T1bc']): ft1w = datain['T1bc'] elif os.path.isdir(datain['MRT1W']): # create file name for the converted NIfTI image fnii = 'converted' run([Cnt['DCM2NIIX'], '-f', fnii, datain['T1nii']]) ft1nii = glob.glob(os.path.join(datain['T1nii'], '*converted*.nii*')) ft1w = ft1nii[0] else: raise IOError('Disaster: no T1w image!') # putput for the T1w in register with PET ft1out = os.path.join(os.path.dirname(ft1w), 'T1w_r' + '.nii.gz') # pext file fo rthe affine transform T1w->PET faff = os.path.join(os.path.dirname(ft1w), fcomment + 'mr2pet_affine' + '.txt') # time.strftime('%d%b%y_%H.%M',time.gmtime()) # > call the registration routine if os.path.isfile(Cnt['REGPATH']): cmd = [ Cnt['REGPATH'], '-ref', recute.fpet, '-flo', ft1w, '-rigOnly', '-speeeeed', '-aff', faff, '-res', ft1out] if log.getEffectiveLevel() > logging.INFO: cmd.append('-voff') run(cmd) else: raise IOError('Path to registration executable is incorrect!') # pet the pCT mu-map with the above faff pmudic = pct_mumap(datain, txLUT_, axLUT_, Cnt, faff=faff, fpet=recute.fpet, fcomment=fcomment) mup = pmudic['im'] muh = muh[2 * Cnt['RNG_STRT']:2 * Cnt['RNG_END'], :, :] mup = mup[2 * Cnt['RNG_STRT']:2 * Cnt['RNG_END'], :, :] return [muh, mup] else: muh = muh[2 * Cnt['RNG_STRT']:2 * Cnt['RNG_END'], :, :] muo = muo[2 * Cnt['RNG_STRT']:2 * Cnt['RNG_END'], :, :] return [muh, muo]