2D sinogram geometry
This page describes the 2D sinogram geometries available in the Julia package Sinograms.jl
.
This page comes from a single Julia file: 02-sino-geom.jl
.
You can access the source code for such Julia documentation using the 'Edit on GitHub' link in the top right. You can view the corresponding notebook in nbviewer here: 02-sino-geom.ipynb
, or open it in binder here: 02-sino-geom.ipynb
.
Setup
Packages needed here.
using Unitful: mm, °
using Plots # these 2 must precede 'using Sinograms' for sino_plot_rays to work
using Sinograms: SinoPar, SinoMoj, SinoFanArc, SinoFanFlat, SinoFan
using Sinograms: sino_plot_rays, rays, angles
using MIRTjim: jim, prompt
using InteractiveUtils: versioninfo
The following line is helpful when running this file as a script; this way it will prompt user to hit a key after each figure is displayed.
isinteractive() ? jim(:prompt, true) : prompt(:draw);
2D Sinogram geometries
To perform 2D image reconstruction from a 2D sinogram, one must first describe how the rays in the sinogram are sampled.
Mathematically, the Radon transform $p(r,\phi)$ of $f(x,y)$ is defined for all $r$ and $ϕ$ but practical systems record $p(r,ϕ)$ only for certain finite sets of samples of those values.
Parallel-beam geometry
The simplest form of sinogram sampling is equally spaced samples in both $r$ and $ϕ$. This sampling is called a parallel-beam geometry. (Very few practical systems have this sampling pattern, but it is an easy place to start.) Both FBP and iterative reconstruction methods need to know which values of $r$ and $ϕ$ are sampled.
In this package, SinoPar
is the type that describes a parallel-beam sinogram geometry.
The built-in defaults provide helpful reminders about the usage.
SinoPar()
SinoPar{Float32, Float32} :
nb::Int64 128
d::Float32 1.0
offset::Float32 0.0
na::Int64 200
orbit::Float32 180.0
orbit_start::Float32 0.0
This package supports units via the Unitful.jl and using units is recommended (but not required).
Here is an example of how to specify a parallel-beam geometry. Everything is a named keyword argument with sensible default values.
orbit
andorbit_start
must both be unitless (degrees) or have the same units (e.g., degrees or radians).- detector spacing
d
(can be unitless or have units e.g., mm). - The projection angles $ϕ$ are equally space and given by
orbit_start + (0:(nb-1))/nb * orbit
.
rg = SinoPar( ;
nb = 64, # number of radial samples ("bins")
na = 30, # number of angular samples
d = 2mm, # detector spacing
offset = 0.25, # quarter detector offset (unitless)
orbit = 180, # angular range (in degrees)
orbit_start = 0, # starting angle (in degrees)
)
SinoPar{Unitful.Quantity{Int64, 𝐋, Unitful.FreeUnits{(mm,), 𝐋, nothing}}, Float32} :
nb::Int64 64
d::Unit{Int64} 2 mm
offset::Float32 0.25
na::Int64 30
orbit::Float32 180.0
orbit_start::Float32 0.0
The struct rg
has numerous useful properties; type ?SinoGeom
to see the full list.
For example, to access the angular samples in degrees type angles(rg)
angles(rg)
0.0f0:6.0f0:174.0f0
The following function visualizes the sampling pattern.
sino_plot_rays(rg; ylims=(0,180), yticks=(0:90:180), widen=true, title="Parallel")
prompt()
Fan-beam CT with an arc detector (3rd generation CT)
For a fan-beam geometry, the arguments are the same as for SinoPar
with the addition of specifying:
dsd
distance from source to detectordod
distance from origin (isocenter) to detectordfs
distance from focal point of detector to source (0 for a 3rd gen arc detector, andInf
for a flat detector)source_offset
for misaligned systems where the ray from the source to the detector center does not intersect the isocenter. Not fully supported; submit an issue if you need this feature.
Here is an example that corresponds to a GE Lightspeed CT system. These numbers are published in IEEE T-MI Oct. 2006, p.1272-1283.
rg = SinoFanArc( ; nb=888, na=984,
d=1.0239mm, offset=1.25, dsd=949.075mm, dod=408.075mm)
SinoFanArc{Unitful.Quantity{Float64, 𝐋, Unitful.FreeUnits{(mm,), 𝐋, nothing}}, Float32} :
nb::Int64 888
d::Unit{Float64} 1.0239 mm
offset::Float32 1.25
na::Int64 984
orbit::Float32 360.0
orbit_start::Float32 0.0
source_offset::Unit{Float64} 0.0 mm
dsd::Unit{Float64} 949.075 mm
dod::Unit{Float64} 408.075 mm
Here is a smaller example for plotting the rays.
rg = SinoFanArc( ; nb=64, na=30,
d=20mm, offset=0.25, dsd=900mm, dod=400mm)
sino_plot_rays(rg; ylims=(-50,400), yticks=(0:180:360), widen=true,
title="Fan-beam for arc detector")
prompt()
Fan-beam CT with a flat detector
This geometry is the same as the arc detector except that dfs=Inf
.
rg = SinoFanFlat( ; nb=64, na=30,
d=20mm, offset=0.25, dsd=900mm, dod=400mm)
SinoFanFlat{Unitful.Quantity{Float32, 𝐋, Unitful.FreeUnits{(mm,), 𝐋, nothing}}, Float32} :
nb::Int64 64
d::Unit{Float32} 20.0f0 mm
offset::Float32 0.25
na::Int64 30
orbit::Float32 360.0
orbit_start::Float32 0.0
source_offset::Unit{Float32} 0.0f0 mm
dsd::Unit{Float32} 900.0f0 mm
dod::Unit{Float32} 400.0f0 mm
Here is its sampling plot
sino_plot_rays(rg; ylims=(-50,400), yticks=(0:180:360), widen=true,
title="Fan-beam for flat detector")
prompt()
Mojette sampling
This is a specialized sampling geometry that is currently incompletely supported.
rg = SinoMoj( ; nb=60, na=30)
SinoMoj{Float32, Float32} :
nb::Int64 60
d::Float32 1.0
offset::Float32 0.0
na::Int64 30
orbit::Float32 180.0
orbit_start::Float32 0.0
Here is its sampling plot
sino_plot_rays(rg; ylims=(0,180), yticks=(0:90:180), widen=true,
title="Mojette sampling")
Here is a diagram that illustrates how the radial spacing is a function of the projection view angle for the Mojette geometry. The key aspect here is that in each image row the line intersection lengths are identical.
plot(xlims=(-1,1) .* 3.5, ylims = (-1,1) .* 2.5, xlabel="x", ylabel="x")
default(label="")
for y in -2:2
plot!([-3, 3], [y, y], color=:black)
end
for x in -3:3
plot!([x, x], [-2, 2], color=:black)
end
plot_ray!(r, ϕ) = plot!(
r*cos(ϕ) .+ [-1, 1] * 4 * sin(ϕ),
r*sin(ϕ) .+ [1, -1] * 4 * cos(ϕ),
color = :blue,
)
ia = 4 # pick an angle
i = rays(rg) # iterator
rs = [i[1] for i in i] # radial samples
ϕs = [i[2] for i in i] # projection angle
r = rs[:,ia]
r = r[abs.(r) .< 3]
ϕ = ϕs[1,ia]
plot_ray!.(r, ϕ)
plot!(aspect_ratio=1, title = "Mojette line integrals")
This page was generated using Literate.jl.