#!/bin/csh -e
# spect,sim,sphere
# simulations of 3D SPECT using ASPIRE.
# examine count recovery for spheres in ellipse background
# for FBP/Chang vs OS-EM with full 3D detector response
# Copyright 2002-04-20, Jeff Fessler, The University of Michigan

which sed > /dev/null	# avoids 'sed not found' message
alias prom 'echo -n " "; echo -n "hit space: "; set dummy = $<'
alias prom 'exit'

set arg = ""
if ($#argv >= 1) then
	set arg = $1 
endif

#set	nx	= 128
set	nx	= 64
set	na	= 60	# projection views
set	nz	= `echo "$nx / 2" | bc`
if 1 then
	set scatperc	= 20	# 20% scatter
	set	vol	= 100	# sphere volume in cc = cm^3 ALL UNITS IN CM!
	set	backg	= 20	# elliptical cylinder background intensity
	set	foreg	= 100	# sphere intensity
	set	dsamp	= 1	# subsampling for partial volume
	set	case	= d$dsamp,v$vol,b$backg
endif

set	ny	= $nx	# required by spect3
set	nu	= $nx	# required by spect3
set	nv	= $nz	# required by spect3
set	orbit	= 360
set	ostart	= 0
set	dx	= `echo "0.7196 * 64 / $nx" | bc -l`	# pixel size in cm
set	dx	= `/usr/bin/printf %g $dx`		# neater
#set	du	= $dx	# ray spacing, must equal dx for 3s
set	count	= 1e6	# irrelevant since noise free

# show images
alias	ji	'echo DISPLAY \!*; j --red -b -1 -nrow 8 \!*'
alias	ji	'echo DISPLAY \!*; op disp -red -b -1 -ncol 8 \!*'
# display sinograms or projection views
#alias	js	'op -chat 0 transpose t9 \!* 1,2; j --red -b -1 -nrow 10'
alias	js	'echo DISPLAY \!*; j --red -b -1 -nrow 10 \!*' # show views
if 0 then	# for slow remote logins, or for users without 'j' script
	alias	j	'op range'
	alias	js	j
	alias	ji	j
endif
set	i	= "i -nthread 2" # because i have dual processors

set	data	= ./out,n$nx,$case
if !(-d $data) mkdir $data

#set	psf	= ./dat/psf,uhe,$nx.fld
set	gpsf	= gauss,0.5,1.2 # fwhm in cm at detector and isocenter

set	voi	= $data/voi.fld
set	xtrue	= $data/xtrue.fld
set	mask	= $data/mask.fld
set	mumap	= $data/mumap.fld
set	proj	= $data/proj,$gpsf.fld
set	ci	= $data/ci.fld
set	ybi	= $data/ybi.fld
set	chang	= $data/chang.fld

set	sfilter = 1
set	spre	= 3s@$dx,$dx,$dx,$orbit,$ostart,$sfilter
#set	stype	= $spre,conv1sym@$mumap@$psf@-$nx,$nz,$na
set	stype	= $spre,$gpsf@$mumap@-@-$nx,$nz,$na
set	stype0	= $spre,none@$mumap@-@-$nx,$nz,$na	# no psf for chang!

#
# voi
#
if ($arg == voi || !(-e $voi)) then
	# sphere radius in pixels from volume and pixel size:
	set r = `echo "e((1/3) * l(3/4 * $vol)) / $dx" | bc -l`
	set x = `echo "-20 * $nx" / 128 | bc -l`
	echo "sphere radius = $r"
	op ellipsoid $voi $nx $ny $nz \
		$x 0 0	$r $r $r	0 0 1 $dsamp
prom
endif


#
# object: background + voi
#
set ell_x = `echo "48 * $nx / 128" | bc`
set ell_y = `echo "32 * $nx / 128" | bc`
if ($arg == xtrue || !(-e $xtrue)) then

	# background ellipses
	set n0 = `echo "$nx / 16" | bc`
	set n1 = `echo "$nx / 16 * 6" | bc`
	op unif t0 $nx $ny $n0 0
	op ellipse t1 $nx $ny 0 0 $ell_x $ell_y 0 $backg $dsamp
	op rep t1 t1 $n1
	op stack t1 float t0 t1 t0	# 8 48 8 -> 64

	set lam = `echo "$foreg - $backg" | bc -l`	# incremental intensity

	op mul t0 $voi - $lam
	op add $xtrue t0 t1

	ji $xtrue
prom
endif


#
# mask
#
if ($arg == mask || (("$mask" != "-") && !(-e "$mask"))) then
	set rx = `echo "$ell_x + 4 * $nx / 128" | bc`
	set ry = `echo "$ell_y + 4 * $nx / 128" | bc`
	op ellipse t0 $nx $ny 0 0 $rx $ry 0 1 1
	op conv $mask t0 byte
	op add t0 $xtrue $mask
	ji t0
prom
endif


#
# attenuation map (mumap): a uniform ellipse
#
set mu = 0.11	# 0.11/cm for iodine 360KeV in water
if ($arg == mumap || (("$mumap" != "-") && !(-e "$mumap"))) then
	op ellipse $mumap $nx $ny 0 0 $ell_x $ell_y 0 $mu 3
	j --red $mumap
prom
endif


#
# PSFs
# here is where we would make customized psfs if needed.
# instead, use built-in gaussian psf model.
#
if 0 then #&& ($arg == psfs || (("$psf" != "-") && !(-e "$psf"))) then
	mat << END
	make_psf_uhe_22cm_128x64
END
	j --red $psf
	op range $psf
prom
endif


#
# noise-free projections
#
if (0 || $arg == proj || !(-e $proj)) then
	time $i proj3 $proj $xtrue $stype $mask
	js $proj
prom
endif


#
# Chang attenuation correction factors (for FBP)
#
if ($arg == chang || !(-e $chang)) then
	$i back3 t0 $nx $ny $nz - - $stype0 -
	op div0 $chang - t0 $na
	op range $chang
	ji $chang
	# compare to 2d approach - agrees since uniform ellipse all slices
	if 0 then
		set rx = `echo "$ell_x * $dx" |  bc`	# radius in mm
		set ry = `echo "$ell_y * $sx" |  bc`	# radius in mm
		op chang t0 $nx $ny $mu 0 0 $rx $ry $na $sx $ostart $orbit
		op range t0 $chang
	endif
prom
endif


#
# FBP/Chang with ramp filter
# trick: use pixel size = 1 for consistency with 3s projector 
#
set fbpwin = boxcar,1,1
set fbp0 = $data/fbp0,$fbpwin.fld
if ($arg == fbp0 || !(-e $fbp0)) then
	op transpose t0 $proj 1,2	# make sinograms nu,na,nv
	op fbp t0 t0 user,t $nx $ny $fbpwin 1 1 0  0 0 $orbit $ostart
	op mul $fbp0 t0 $chang
	op range $xtrue $fbp0
	ji $fbp0
prom
endif


#
# look at sensitivity pattern
# fix: the end slices are about 1% more sensitive - WHY?
#
if ($arg == sens) then
	op unif t0 $nu $nv $na
	$i back3 t0 $nx $ny $nz t0 - $stype $mask
	op mul t1 t0 $chang	# apply chang for fun
	op sub t2 t1 - $na	# subtract # of angles to make near 0
	j --red t2
prom
endif


#
# noise-free iterative reconstructions
# initialize with uniform image?????????????????????????????
#
if ($backg == 0) then
	set	init	= -$nx,$ny,$nz,1
else
	set	init	= -$nx,$ny,$nz,$backg
endif


#
# emulate effects of scatter
#
if 1 then
	set ri = $data/ri,$scatperc.fld
	if !(-e $ri) then
		op blur t0 $proj 10,20,zer 10,20,zer 1,1,zer
		op mul t0 t0 - 0.01
		op mul $ri t0 - $scatperc

		op add t99 $proj $ri
	endif
	set yi = t99
else
	set yi = $proj
endif


#
# EM and OSEM noise-free
# note: EM converges *much* faster for the point source
# with shift=tiny rather than shift=1 !
# shift=0 causes numerical precision problems (overflow?)
# set nsubset=1 for EM
#
set	niter	= 20
set	niter	= 2
set	penal	= -
set	nsubset	= 10
set	nsubset	= 5
set	otype	= osemc	# usually use this!
set	alg	= $otype,fast,$nsubset,$na,1.0
set	em0	= $data/${otype},s$scatperc,n$nsubset,i$niter.fld
set	method	= @$niter@$alg@$penal
set	saver	= stack,1
if ($arg == em0 || !(-e $em0)) then
	i -chat 15 -nthread 2 empl3 $em0 $init $yi - 1 $ri 1 $stype $mask \
		$method $saver 0 1 1e9 0 -
	op range $em0 $xtrue
	ji $em0
#prom
endif

exit ##################################################################
