def frwd_prj(im, scanner_params, isub=np.array([-1], dtype=np.int32), dev_out=False, attenuation=False): ''' Calculate forward projection (a set of sinograms) for the provided input image. Arguments: im -- input image (can be emission or mu-map image). scanner_params -- dictionary of all scanner parameters, containing scanner constants, transaxial and axial look up tables (LUT). isub -- array of transaxial indices of all sinograms (angles x bins) used for subsets. when the first element is negative, all transaxial bins are used (as in pure EM-ML). dev_out -- if True, output sinogram is in the device form, i.e., with two dimensions (# bins/angles, # sinograms) instead of default three (# sinograms, # bins, # angles). attenuation -- controls whether emission or LOR attenuation probability sinogram is calculated; the default is False, meaning emission sinogram; for attenuation calculations (attenuation=True), the exponential of the negative of the integrated mu-values along LOR path is taken at the end. ''' log = logging.getLogger(__name__) # Get particular scanner parameters: Constants, transaxial and axial LUTs Cnt = scanner_params['Cnt'] txLUT = scanner_params['txLUT'] axLUT = scanner_params['axLUT'] #>choose between attenuation forward projection (mu-map is the input) #>or the default for emission image forward projection if attenuation: att = 1 else: att = 0 if Cnt['SPN'] == 1: # number of rings calculated for the given ring range (optionally we can use only part of the axial FOV) NRNG_c = Cnt['RNG_END'] - Cnt['RNG_STRT'] # number of sinos in span-1 nsinos = NRNG_c**2 # correct for the max. ring difference in the full axial extent (don't use ring range (1,63) as for this case no correction) if NRNG_c == 64: nsinos -= 12 elif Cnt['SPN'] == 11: nsinos = Cnt['NSN11'] elif Cnt['SPN'] == 0: nsinos = Cnt['NSEG0'] if im.shape[0] == Cnt['SO_IMZ'] and im.shape[1] == Cnt[ 'SO_IMY'] and im.shape[2] == Cnt['SO_IMX']: ims = mmrimg.convert2dev(im, Cnt) elif im.shape[0] == Cnt['SZ_IMX'] and im.shape[1] == Cnt[ 'SZ_IMY'] and im.shape[2] == Cnt['SZ_IMZ']: ims = im elif im.shape[0] == Cnt['rSO_IMZ'] and im.shape[1] == Cnt[ 'SO_IMY'] and im.shape[2] == Cnt['SO_IMX']: ims = mmrimg.convert2dev(im, Cnt) elif im.shape[0] == Cnt['SZ_IMX'] and im.shape[1] == Cnt[ 'SZ_IMY'] and im.shape[2] == Cnt['rSZ_IMZ']: ims = im else: log.error( 'wrong image size; it has to be one of these: (z,y,x) = (127,344,344) or (y,x,z) = (320,320,128)' ) log.debug('number of sinos:%d' % nsinos) #predefine the sinogram. if subsets are used then only preallocate those bins which will be used. if isub[0] < 0: sinog = np.zeros((txLUT['Naw'], nsinos), dtype=np.float32) else: sinog = np.zeros((len(isub), nsinos), dtype=np.float32) # -------------------- petprj.fprj(sinog, ims, txLUT, axLUT, isub, Cnt, att) # -------------------- # get the sinogram bins in a proper sinogram sino = np.zeros((txLUT['Naw'], nsinos), dtype=np.float32) if isub[0] >= 0: sino[isub, :] = sinog else: sino = sinog # put the gaps back to form displayable sinogram if not dev_out: sino = mmraux.putgaps(sino, txLUT, Cnt) return sino
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 osemone(datain, mumaps, hst, scanner_params, recmod=3, itr=4, fwhm=0., mask_radius=29., sctsino=np.array([]), outpath='', store_img=False, frmno='', fcomment='', store_itr=[], emmskS=False, ret_sinos=False, attnsino=None, randsino=None, normcomp=None): #---------- sort out OUTPUT ------------ #-output file name for the reconstructed image, initially assume n/a fout = 'n/a' if store_img or store_itr: if outpath == '': opth = os.path.join(datain['corepath'], 'reconstructed') else: opth = outpath mmraux.create_dir(opth) if ret_sinos: return_ssrb = True return_mask = True else: return_ssrb = False return_mask = False #---------- # Get particular scanner parameters: Constants, transaxial and axial LUTs Cnt = scanner_params['Cnt'] txLUT = scanner_params['txLUT'] axLUT = scanner_params['axLUT'] import time from niftypet import nipet # from niftypet.nipet.sct import mmrsct # from niftypet.nipet.prj import mmrhist if Cnt['VERBOSE']: print 'i> reconstruction in mode', recmod # get object and hardware mu-maps muh, muo = mumaps # get the GPU version of the image dims mus = mmrimg.convert2dev(muo + muh, Cnt) if Cnt['SPN'] == 1: snno = Cnt['NSN1'] elif Cnt['SPN'] == 11: snno = Cnt['NSN11'] # remove gaps from the prompt sino psng = mmraux.remgaps(hst['psino'], txLUT, Cnt) #========================================================================= # GET NORM #------------------------------------------------------------------------- if normcomp == None: ncmp, _ = mmrnorm.get_components(datain, Cnt) else: ncmp = normcomp print 'w> using user-defined normalisation components' nsng = mmrnorm.get_sinog(datain, hst, axLUT, txLUT, Cnt, normcomp=ncmp) #========================================================================= #========================================================================= # ATTENUATION FACTORS FOR COMBINED OBJECT AND BED MU-MAP #------------------------------------------------------------------------- #> combine attenuation and norm together depending on reconstruction mode if recmod == 0: asng = np.ones(psng.shape, dtype=np.float32) else: #> check if the attenuation sino is given as an array if isinstance(attnsino, np.ndarray) \ and attnsino.shape==(Cnt['NSN11'], Cnt['NSANGLES'], Cnt['NSBINS']): asng = mmraux.remgaps(attnsino, txLUT, Cnt) print 'i> using provided attenuation factor sinogram' elif isinstance(attnsino, np.ndarray) \ and attnsino.shape==(Cnt['Naw'], Cnt['NSN11']): asng = attnsino print 'i> using provided attenuation factor sinogram' else: asng = np.zeros(psng.shape, dtype=np.float32) petprj.fprj(asng, mus, txLUT, axLUT, np.array([-1], dtype=np.int32), Cnt, 1) #> combine attenuation and normalisation ansng = asng * nsng #========================================================================= #========================================================================= # Randoms #------------------------------------------------------------------------- if isinstance(randsino, np.ndarray): rsino = randsino rsng = mmraux.remgaps(randsino, txLUT, Cnt) else: rsino, snglmap = nipet.randoms(hst, scanner_params) rsng = mmraux.remgaps(rsino, txLUT, Cnt) #========================================================================= #========================================================================= # SCAT #------------------------------------------------------------------------- if recmod == 2: if sctsino.size > 0: ssng = mmraux.remgaps(sctsino, txLUT, Cnt) elif sctsino.size == 0 and os.path.isfile(datain['em_crr']): emd = nimpa.getnii(datain['em_crr']) ssn = nipet.vsm(datain, mumaps, emd['im'], hst, rsino, scanner_params, prcnt_scl=0.1, emmsk=False) ssng = mmraux.remgaps(ssn, txLUT, Cnt) else: print 'e> no emission image available for scatter estimation! check if it' 's present or the path is correct.' sys.exit() else: ssng = np.zeros(rsng.shape, dtype=rsng.dtype) #========================================================================= if Cnt['VERBOSE']: print '\n>------ OSEM (', itr, ') -------\n' #------------------------------------ Sn = 14 # number of subsets #-get one subset to get number of projection bins in a subset Sprj, s = get_subsets14(0, scanner_params) Nprj = len(Sprj) #-init subset array and sensitivity image for a given subset sinoTIdx = np.zeros((Sn, Nprj + 1), dtype=np.int32) #-init sensitivity images for each subset imgsens = np.zeros((Sn, Cnt['SZ_IMY'], Cnt['SZ_IMX'], Cnt['SZ_IMZ']), dtype=np.float32) for n in range(Sn): sinoTIdx[n, 0] = Nprj #first number of projection for the given subset sinoTIdx[n, 1:], s = get_subsets14(n, scanner_params) # sensitivity image petprj.bprj(imgsens[n, :, :, :], ansng[sinoTIdx[n, 1:], :], txLUT, axLUT, sinoTIdx[n, 1:], Cnt) #------------------------------------- #-mask for reconstructed image. anything outside it is set to zero msk = mmrimg.get_cylinder( Cnt, rad=mask_radius, xo=0, yo=0, unival=1, gpu_dim=True) > 0.9 #-init image img = np.ones((Cnt['SZ_IMY'], Cnt['SZ_IMX'], Cnt['SZ_IMZ']), dtype=np.float32) #-decay correction lmbd = np.log(2) / resources.riLUT[Cnt['ISOTOPE']]['thalf'] if Cnt['DCYCRR'] and 't0' in hst and 'dur' in hst: dcycrr = np.exp(lmbd * hst['t0']) * lmbd * hst['dur'] / ( 1 - np.exp(-lmbd * hst['dur'])) # apply quantitative correction to the image qf = ncmp['qf'] / resources.riLUT[Cnt['ISOTOPE']]['BF'] / float( hst['dur']) qf_loc = ncmp['qf_loc'] elif not Cnt['DCYCRR'] and 't0' in hst and 'dur' in hst: dcycrr = 1. # apply quantitative correction to the image qf = ncmp['qf'] / resources.riLUT[Cnt['ISOTOPE']]['BF'] / float( hst['dur']) qf_loc = ncmp['qf_loc'] else: dcycrr = 1. qf = 1. qf_loc = 1. #-affine matrix for the reconstructed images B = mmrimg.image_affine(datain, Cnt) #-time it stime = time.time() # import pdb; pdb.set_trace() #========================================================================= # OSEM RECONSTRUCTION #------------------------------------------------------------------------- for k in trange(itr, disable=not Cnt['VERBOSE'], desc="OSEM"): petprj.osem(img, msk, psng, rsng, ssng, nsng, asng, imgsens, txLUT, axLUT, sinoTIdx, Cnt) if np.nansum(img) < 0.1: print '---------------------------------------------------------------------' print 'w> it seems there is not enough true data to render reasonable image.' print '---------------------------------------------------------------------' #img[:]=0 itr = k break if recmod >= 3 and (((k < itr - 1) and (itr > 1))): # or (itr==1) sct_time = time.time() sct = nipet.vsm(datain, mumaps, mmrimg.convert2e7(img, Cnt), hst, rsino, scanner_params, emmsk=emmskS, return_ssrb=return_ssrb, return_mask=return_mask) if isinstance(sct, dict): ssn = sct['sino'] else: ssn = sct ssng = mmraux.remgaps(ssn, txLUT, Cnt) if Cnt['VERBOSE']: print 'i> scatter time:', (time.time() - sct_time) # save images during reconstruction if requested if store_itr and k in store_itr: im = mmrimg.convert2e7(img * (dcycrr * qf * qf_loc), Cnt) fout = os.path.join(opth, os.path.basename(datain['lm_bf'])[:8] \ + frmno +'_t'+str(hst['t0'])+'-'+str(hst['t1'])+'sec' \ +'_itr'+str(k)+fcomment+'_inrecon.nii.gz') nimpa.array2nii(im[::-1, ::-1, :], B, fout) if Cnt['VERBOSE']: print 'i> recon time:', (time.time() - stime) #========================================================================= if Cnt['VERBOSE']: print 'i> applying decay correction of', dcycrr print 'i> applying quantification factor', qf, 'to the whole image for the frame duration of :', hst[ 'dur'] img *= dcycrr * qf * qf_loc #additional factor for making it quantitative in absolute terms (derived from measurements) #---- save images ----- #-first convert to standard mMR image size im = mmrimg.convert2e7(img, Cnt) #-description text to NIfTI #-attenuation number: if only bed present then it is 0.5 attnum = (1 * (np.sum(muh) > 0.5) + 1 * (np.sum(muo) > 0.5)) / 2. descrip = 'alg=osem'+ \ ';sub=14'+ \ ';att='+str(attnum*(recmod>0))+ \ ';sct='+str(1*(recmod>1))+ \ ';spn='+str(Cnt['SPN'])+ \ ';itr='+str(itr) +\ ';fwhm='+str(fwhm) +\ ';t0='+str(hst['t0']) +\ ';t1='+str(hst['t1']) +\ ';dur='+str(hst['dur']) +\ ';qf='+str(qf) if fwhm > 0: im = ndi.filters.gaussian_filter(im, fwhm2sig(fwhm, Cnt), mode='mirror') if store_img: fout = os.path.join(opth, os.path.basename(datain['lm_bf'])[:8] \ + frmno +'_t'+str(hst['t0'])+'-'+str(hst['t1'])+'sec' \ +'_itr'+str(itr)+fcomment+'.nii.gz') if Cnt['VERBOSE']: print 'i> saving image to: ', fout nimpa.array2nii(im[::-1, ::-1, :], B, fout, descrip=descrip) # returning: # (0) E7 image [can be smoothed]; # (1) file name of saved E7 image # (2) [optional] scatter sino # (3) [optional] single slice rebinned scatter # (4) [optional] mask for scatter scaling based on attenuation data # (5) [optional] random sino # if ret_sinos and recmod>=3: # recout = namedtuple('recout', 'im, fpet, ssn, sssr, amsk, rsn') # recout.im = im # recout.fpet = fout # recout.ssn = ssn # recout.sssr = sssr # recout.amsk = amsk # recout.rsn = rsino # else: # recout = namedtuple('recout', 'im, fpet') # recout.im = im # recout.fpet = fout if ret_sinos and recmod >= 3 and itr > 1: RecOut = namedtuple('RecOut', 'im, fpet, affine, ssn, sssr, amsk, rsn') recout = RecOut(im, fout, B, ssn, sct['ssrb'], sct['mask'], rsino) else: RecOut = namedtuple('RecOut', 'im, fpet, affine') recout = RecOut(im, fout, B) return recout