| SOCR ≫ | BPAD1 Website ≫ | BPAD GitHub ≫ |
Chapter 5 sent photons through the patient and measured what survived. This chapter reverses the geometry: the source is inside the patient, and the image is a map not of tissue structure but of where an injected molecule went. That inversion buys picomolar molecular sensitivity and costs almost everything else, resolution, counts, and speed. Understanding why those costs are unavoidable is the point of the chapter.
Nuclear medicine images physiology and molecular processes. A radiotracer, in quantities far too small to perturb the system it probes, is injected; it distributes according to blood flow, transport, binding, and metabolism; its decaying nuclei emit photons that escape the body; and reconstruction produces a three-dimensional map of tracer concentration that kinetic models convert into quantitative measures of function.
The conceptual sequence is
\[\underbrace{\text{produce tracer}}_{\text{reactor, cyclotron, generator}} \rightarrow \underbrace{\text{inject and distribute}}_{\text{biology}} \rightarrow \underbrace{\text{nuclei decay}}_{\gamma\ \text{or}\ \beta^+} \rightarrow \underbrace{\text{detect photons}}_{\text{collimator or coincidence}}\]
\[\rightarrow \underbrace{\text{reconstruct}}_{\text{MLEM / OSEM}} \rightarrow \underbrace{\text{correct}}_{\text{attenuation, scatter, partial volume}} \rightarrow \underbrace{\text{quantify}}_{\mathrm{SUV},\ K_i} \rightarrow \underbrace{\text{justify the dose}}_{\text{MIRD, ALARA}}\]
Table 1 maps each earlier result the chapter depends on to the section that uses it. Every section closes with a slide-ready Section summary and a Checkpoint question, and three computational laboratories build the signature skills: coincidence statistics and NECR, statistical reconstruction, and tracer kinetics.
Chapter-level clinical puzzle. An oncologist needs to know whether a 7 mm lung nodule is malignant. A cardiologist must distinguish scarred myocardium from muscle that would recover after revascularization. A neurologist wants to know whether a patient with memory loss has amyloid pathology. And in every case someone must answer: how much of the reported number is real, and how much is blur, scatter, or noise? The last question is sharper here than anywhere else in this book, because emission images are built from a million times fewer photons than a CT.
Photon story for the chapter. Follow one nucleus. An \(^{18}\)F atom, chemically bonded into a glucose analogue, has been carried by the bloodstream into a tumour cell and phosphorylated so that it cannot leave. Minutes later it decays, emitting a positron. The positron travels about half a millimetre, slows, and annihilates with an electron; their combined rest mass reappears as two 511 keV photons flying apart in almost exactly opposite directions. Both must escape 20 cm of tissue, a 15% chance, and both must strike detectors within a few nanoseconds of each other. That single surviving pair defines one line in space. Several hundred million such lines, corrected for attenuation and scatter and reconstructed under a Poisson likelihood, become one number on a report: SUV 8.4.
Key idea. CT is transmission imaging: it measures how tissue blocks an external beam, producing a map of \(\mu(x,y,z)\), anatomy. SPECT and PET are emission imaging: they measure photons coming out of an injected tracer, producing a map of concentration \(C(x,y,z)\), function. The reconstruction mathematics is shared; the physics, the noise, and the clinical question are not.
| # | Objective | Section |
|---|---|---|
| 1 | Distinguish transmission from emission imaging and state what each reconstructs. | 6.1 |
| 2 | Apply the decay law; compute physical, biological, and effective half-lives. | 6.2 |
| 3 | Compute cumulated activity and connect effective half-life to absorbed dose. | 6.2, 6.11 |
| 4 | Explain reactor, cyclotron, and generator production, and solve the Bateman equation for the Mo-99/Tc-99m generator. | 6.3 |
| 5 | State the tracer principle and the design requirements of a radiopharmaceutical. | 6.3 |
| 6 | Explain Anger logic and energy windowing, and compute collimator resolution and geometric efficiency. | 6.4 |
| 7 | Quantify the collimator resolution–sensitivity trade-off and explain why SPECT discards >99.9% of photons. | 6.4 |
| 8 | Explain annihilation, coincidence detection, and electronic collimation. | 6.5 |
| 9 | Use Compton kinematics to explain why energy-based scatter rejection is far weaker at 511 keV than at 140 keV. | 6.5 |
| 10 | Compute random coincidence rates and locate the NECR peak. | 6.5 |
| 11 | Apply the PET resolution budget equation and identify which term dominates for a given isotope and scanner. | 6.6 |
| 12 | Compute time-of-flight localization and the resulting SNR gain. | 6.6 |
| 13 | Explain why emission data are Poisson and derive the MLEM update. | 6.7 |
| 14 | Demonstrate that MLEM trades bias against variance with iteration number, and explain why iterations are stopped early. | 6.7 |
| 15 | Show that PET attenuation correction is position-independent while SPECT correction is not. | 6.8 |
| 16 | Build a two-tissue compartment model, linearize it with the Patlak plot, and compute SUV. | 6.9 |
| 17 | Quantify partial-volume loss with recovery coefficients and correct SUV for lesion size. | 6.10 |
| 18 | Apply the MIRD formalism, compute effective dose from administered activity, and compare with CT. | 6.11 |
| 19 | Connect FDG, perfusion, amyloid/tau, and theranostic agents to clinical decisions. | 6.12 |
| 20 | Implement NECR, MLEM, and kinetic analysis in R. |
6.14 |
| Symbol | Meaning | Units |
|---|---|---|
| \(N,\ \lambda,\ A\) | number of nuclei; decay constant; activity \(A=\lambda N\) | —, s\(^{-1}\), Bq |
| \(T_{1/2},\ T_p,\ T_b,\ T_e\) | half-life; physical, biological, effective | h |
| \(\tilde A\) | cumulated activity (total decays) | MBq h |
| \(f\) | branching fraction | , |
| \(d,\ L,\ t,\ b\) | collimator hole diameter, length, septal thickness; source distance | mm |
| \(R_{\text{coll}},\ g\) | collimator geometric resolution; geometric efficiency | mm,, |
| \(m_ec^2\) | electron rest energy \(=511\) keV | keV |
| \(S_1,S_2,\ 2\tau\) | singles rates; coincidence window | s\(^{-1}\), s |
| \(T,\ S,\ R\) | true, scatter, random coincidence rates | s\(^{-1}\) |
| NECR | noise-equivalent count rate | s\(^{-1}\) |
| SF | scatter fraction \(S/(T+S)\) | , |
| \(D_{\text{ring}}\) | detector ring diameter | mm |
| \(\Delta t,\ \Delta x\) | coincidence timing resolution; TOF localization | ps, cm |
| \(a_{ij},\ x_j,\ y_i\) | system matrix; image voxel; measured counts | , |
| \(\mu\) | linear attenuation coefficient | cm\(^{-1}\) |
| ACF | attenuation correction factor \(e^{\mu D}\) | , |
| \(C_p(t)\) | arterial input function (plasma concentration) | kBq mL\(^{-1}\) |
| \(K_1,k_2,k_3,k_4\) | delivery, washout, trapping, release rate constants | mL cm\(^{-3}\)min\(^{-1}\); min\(^{-1}\) |
| \(K_i\) | net influx constant \(K_1k_3/(k_2+k_3)\) | min\(^{-1}\) |
| SUV, SUL | standardized uptake value; lean-body-mass variant | , |
| RC | recovery coefficient (partial-volume) | , |
| \(\tilde S\) | MIRD S-value, dose per unit cumulated activity | mGy (MBq s)\(^{-1}\) |
| \(E_{\text{eff}}\) | effective dose | mSv |
| Earlier result | Chapter | Role here | Section |
|---|---|---|---|
| First-order ODE and exponential decay | 1 | radioactive decay law; biological clearance | 6.2 |
| Coupled linear ODE systems | 1 | Bateman generator equations; compartment models | 6.3, 6.9 |
| Poisson counting statistics, \(\sqrt{N}\) | 1, 5 | the dominant noise source in every emission image | 6.2, 6.7 |
| Maximum likelihood and EM | 1, 8 | the MLEM reconstruction update | 6.7 |
| Bias–variance decomposition | 1, 8 | why MLEM iterations are stopped early | 6.7 |
| Compartmental pharmacokinetics | 1 | tracer kinetics; input function; \(K_i\) | 6.9 |
| Linear regression | 1 | Patlak linearization | 6.9 |
| Convolution and point spread function | 1, 3, 4, 5 | partial-volume effect and recovery coefficients | 6.10 |
| Beer–Lambert attenuation | 2, 5 | photon escape from the body; the \(\mu\)-map | 6.8 |
| Compton scattering kinematics | 5 | why 511 keV scatter rejection is weak | 6.5 |
| Radon transform and sinograms | 5 | identical data structure for SPECT and PET | 6.7 |
| Central slice theorem, FBP | 5 | analytic reconstruction and why it fails here | 6.7 |
| CT Hounsfield units and \(\mu\) | 5 | CT-derived attenuation map rescaled to 511 keV | 6.8 |
| Absorbed, equivalent, effective dose | 5 | internal dosimetry; comparison of PET with CT dose | 6.11 |
| Acquisition shift and biomarker validity | 8 | SUV reproducibility across scanners and protocols | 6.10 |
Three modalities share the reconstruction mathematics of Chapter 5, recovering \(f(x,y)\) from projections \(P(\theta,s)\), but differ in what \(f\) means and where the photons originate.
| Property | CT (Ch. 5) | SPECT | PET |
|---|---|---|---|
| Photon source | external X-ray tube | injected \(\gamma\) emitter | injected \(\beta^+\) emitter |
| Reconstructed \(f\) | \(\mu(x,y,z)\) | \(C(x,y,z)\) | \(C(x,y,z)\) |
| Information | anatomy | function | function, metabolism |
| Direction determined by | source–detector geometry | physical collimator | coincidence timing |
| Photons detected | \(\sim10^{9}\) per slice | \(\sim10^{-4}\) of emitted | \(\sim10^{-2}\) of emitted |
| Typical resolution | 0.5 mm | 8–12 mm | 4–6 mm |
| Molecular sensitivity | millimolar | nanomolar | picomolar |
| Dominant noise | quantum, but count-rich | Poisson, count-starved | Poisson, count-starved |
The trade in one line. PET detects roughly \(10^{-2}\) of the photons emitted from a picomolar tracer; CT detects a torrent of photons but can only see millimolar concentration differences. Nuclear medicine buys ten orders of magnitude of molecular sensitivity and pays for it in resolution and noise. That is why PET/CT exists: each modality supplies precisely what the other cannot.
Radioactive decay is random and memoryless: each nucleus has a fixed probability per unit time \(\lambda\) of decaying, independent of its age. For a large population,
\[\begin{equation} \frac{dN}{dt} = -\lambda N \quad\Longrightarrow\quad N(t) = N_0 e^{-\lambda t}, \qquad A(t) = \lambda N(t) = A_0 e^{-\lambda t}, \qquad T_{1/2} = \frac{\ln 2}{\lambda}. \tag{1} \end{equation}\]
Activity \(A\) is decays per second, measured in becquerels (\(1\) Bq \(=1\) s\(^{-1}\)) or curies (\(1\) Ci \(=3.7\times10^{10}\) Bq). Clinical administered activities are tens to thousands of megabecquerels.
Because decay is a counting process, the number of events in a fixed interval is Poisson distributed (Chapter 1): with mean \(\bar n\) counts the standard deviation is \(\sqrt{\bar n}\), so
\[\begin{equation} \mathrm{SNR} = \frac{\bar n}{\sqrt{\bar n}} = \sqrt{\bar n} . \tag{2} \end{equation}\]
Equation (2) is the single most important fact about nuclear medicine image quality, and it is the same \(\sqrt{N}\) law that governed X-ray quantum noise in Chapter 5. The difference is scale: a CT slice is built from \(10^9\) photons and a PET slice from \(10^5\)–\(10^6\), so nuclear images are inescapably noisier.
Isomeric transition (SPECT). A metastable nucleus drops to a lower energy state and emits a single gamma photon. Technetium-99m emits a 140 keV photon, high enough to escape the body, low enough to be stopped efficiently in a NaI(Tl) crystal, and unaccompanied by particulate emission that would add dose without adding signal.
Positron emission (PET). A proton-rich nucleus converts a proton into a neutron, emitting a positron and a neutrino. The positron slows and annihilates with an electron, converting the pair’s rest mass into two photons:
\[\begin{equation} e^{+} + e^{-} \rightarrow 2\gamma, \qquad E_\gamma = m_ec^{2} = 511\ \text{keV}, \qquad \Delta\theta \approx 180^\circ . \tag{3} \end{equation}\]
The photon energy is not a design choice but a constant of nature: it is the electron rest energy. Every PET scanner ever built is optimized around 511 keV for that reason.
Electron capture competes with positron emission and yields characteristic X-rays and gammas used by SPECT agents such as I-123, In-111, and Tl-201.
nuclides$hl_h <- nuclides$half_life_min/60
ggplot(nuclides, aes(hl_h, photon_keV, colour = modality, shape = production)) +
annotate("rect", xmin = 20/60, xmax = 200, ymin = 50, ymax = 700,
fill = bpad_pal[3], alpha = 0.10) +
geom_point(size = 3) +
geom_text(aes(label = nuclide), vjust = -1.0, size = 2.9, show.legend = FALSE) +
scale_x_log10(breaks = c(0.01, 0.1, 1, 10, 100),
labels = c("0.6 min", "6 min", "1 h", "10 h", "100 h")) +
scale_y_log10(breaks = c(70, 140, 200, 364, 511)) +
scale_colour_manual(values = bpad_pal[c(1,2,3)]) +
coord_cartesian(ylim = c(50, 900)) +
labs(x = "Physical half-life (log scale)", y = "Principal photon energy (keV, log scale)",
title = "Imaging radionuclides: half-life versus photon energy",
subtitle = "shaded: the practical operating window")Figure 1: The clinical radionuclide landscape. Half-life is plotted against photon energy on logarithmic axes, with symbol shape indicating production route. The PET isotopes all sit at exactly 511 keV because that energy is fixed by the electron rest mass, Eq. (annihilation); the SPECT isotopes spread across 70 to 364 keV because their photon energies are set by nuclear structure. The shaded band marks the half-life window, roughly 20 minutes to a few days, within which an isotope is simultaneously long enough to survive delivery and short enough to limit dose.
knitr::kable(nuclides[, c("nuclide","mode","half_life_min","photon_keV",
"production","modality")],
col.names = c("Nuclide","Decay mode","Half-life (min)","Photon (keV)",
"Production","Use"),
caption = "Clinically important radionuclides. Note that every PET isotope emits at 511 keV regardless of its identity, because the photon energy comes from annihilation rather than from the nucleus.")| Nuclide | Decay mode | Half-life (min) | Photon (keV) | Production | Use |
|---|---|---|---|---|---|
| F-18 | beta+ | 109.80 | 511 | cyclotron | PET |
| C-11 | beta+ | 20.40 | 511 | cyclotron | PET |
| N-13 | beta+ | 9.97 | 511 | cyclotron | PET |
| O-15 | beta+ | 2.04 | 511 | cyclotron | PET |
| Ga-68 | beta+ | 67.70 | 511 | generator | PET |
| Rb-82 | beta+ | 1.27 | 511 | generator | PET |
| Tc-99m | IT | 360.00 | 140 | generator | SPECT |
| I-123 | EC | 795.00 | 159 | cyclotron | SPECT |
| In-111 | EC | 4034.00 | 171 | cyclotron | SPECT |
| Tl-201 | EC | 4380.00 | 71 | cyclotron | SPECT |
| I-131 | beta- | 11563.00 | 364 | reactor | therapy |
| Lu-177 | beta- | 9576.00 | 208 | reactor | therapy |
cat(sprintf("O-15 half-life is %.1f min: the cyclotron must be within seconds of the scanner.\n",
nuclides$half_life_min[nuclides$nuclide == "O-15"]))## O-15 half-life is 2.0 min: the cyclotron must be within seconds of the scanner.
cat(sprintf("F-18 half-life is %.0f min, long enough to ship regionally: this single fact\n",
nuclides$half_life_min[nuclides$nuclide == "F-18"]))## F-18 half-life is 110 min, long enough to ship regionally: this single fact
## is why FDG, and not a better tracer, became the dominant clinical PET agent.
Two clocks remove activity from a tissue: nuclear decay (physical half-life \(T_p\)) and biological clearance (\(T_b\)). Because the two removal rates add,
\[\begin{equation} \lambda_e = \lambda_p + \lambda_b \quad\Longrightarrow\quad \frac{1}{T_e} = \frac{1}{T_p} + \frac{1}{T_b}, \qquad T_e = \frac{T_p T_b}{T_p + T_b} < \min(T_p, T_b). \tag{4} \end{equation}\]
The absorbed dose depends not on the activity at any instant but on the total number of decays that occur in the tissue, the cumulated activity:
\[\begin{equation} \tilde A = \int_0^{\infty} A(t)\,dt = \frac{A_0}{\lambda_e} = \frac{A_0 T_e}{\ln 2} = 1.443\, A_0 T_e . \tag{5} \end{equation}\]
Equation (5) is the quantitative statement behind the common remark that “the effective half-life governs the dose”: dose is linear in \(T_e\). Halving the effective half-life halves the dose. Section 6.11 completes the chain from \(\tilde A\) to millisieverts.
lam <- function(th) ln2/th
tg <- seq(0, 24, by = 0.05)
show <- c("F-18","Tc-99m","Ga-68","I-131")
dec <- do.call(rbind, lapply(show, function(n) {
th <- nuclides$half_life_min[nuclides$nuclide == n]/60
data.frame(t = tg, A = exp(-lam(th)*tg),
nuclide = factor(sprintf("%s (%.1f h)", n, th),
levels = sapply(show, function(m)
sprintf("%s (%.1f h)", m,
nuclides$half_life_min[nuclides$nuclide==m]/60))))
}))
p_dec <- ggplot(dec, aes(t, A, colour = nuclide)) +
geom_hline(yintercept = 0.5, linetype = "dotted", colour = "grey55") +
geom_line(linewidth = 0.9) +
scale_colour_manual(values = bpad_pal[1:4]) +
labs(x = "Time (hours)", y = "Fraction of activity remaining",
title = "Physical decay")
Tp <- 6.0; Tb <- 24.0; Te <- Tp*Tb/(Tp + Tb)
eff <- rbind(
data.frame(t = tg, A = exp(-lam(Tp)*tg), q = sprintf("physical (%.1f h)", Tp)),
data.frame(t = tg, A = exp(-lam(Tb)*tg), q = sprintf("biological (%.0f h)", Tb)),
data.frame(t = tg, A = exp(-lam(Te)*tg), q = sprintf("effective (%.1f h)", Te)))
p_eff <- ggplot(eff, aes(t, A, colour = q)) +
geom_line(linewidth = 0.95) +
scale_colour_manual(values = bpad_pal[c(1,3,2)]) +
labs(x = "Time (hours)", y = "Fraction remaining",
title = "Tc-99m: rates add")
Te_g <- seq(0.25, 12, by = 0.05)
p_cum <- ggplot(data.frame(Te = Te_g, Acum = 1.443*370*Te_g), aes(Te, Acum)) +
geom_line(linewidth = 0.95, colour = bpad_pal[2]) +
geom_point(data = data.frame(Te = c(1.5, 4.8), Acum = 1.443*370*c(1.5, 4.8)),
size = 2.4, colour = bpad_pal[1]) +
geom_text(data = data.frame(Te = c(1.5, 4.8), Acum = 1.443*370*c(1.5, 4.8)),
aes(label = sprintf("%.0f MBq h", Acum)), hjust = -0.1, size = 2.8) +
labs(x = expression("Effective half-life "*T[e]*" (h)"),
y = expression("Cumulated activity "*tilde(A)*" (MBq h)"),
title = "Dose is linear in effective half-life",
subtitle = expression(A[0]*" = 370 MBq"))
bpad_grid(p_dec, p_eff, p_cum, ncol = 3)Figure 2: Decay clocks and the dose they imply. Left: activity remaining for four imaging radionuclides across a 24 hour window; F-18 and Tc-99m decay substantially during a working day while I-131 barely moves. Centre: for Tc-99m the effective decay, Eq. (effective-halflife), is faster than either the physical or the biological curve alone, because the removal rates add. Right: cumulated activity, Eq. (cumulated-activity), which is the area under the activity curve and is directly proportional to absorbed dose; it rises linearly with effective half-life, so shortening biological clearance is as effective a dose-reduction strategy as choosing a shorter-lived isotope.
cat(sprintf("Tc-99m: T_p = %.1f h, T_b = %.0f h -> T_e = %.2f h (shorter than both)\n",
Tp, Tb, Te))## Tc-99m: T_p = 6.0 h, T_b = 24 h -> T_e = 4.80 h (shorter than both)
## Cumulated activity for 370 MBq at T_e = 4.80 h: 2563 MBq h
cat(sprintf("If biological clearance were halved (T_b = %.0f h), T_e falls to %.2f h\n",
Tb/2, Tp*(Tb/2)/(Tp + Tb/2)))## If biological clearance were halved (T_b = 12 h), T_e falls to 4.00 h
cat(sprintf("and the dose falls by %.0f%% -- with no change to the isotope.\n",
100*(1 - (Tp*(Tb/2)/(Tp + Tb/2))/Te)))## and the dose falls by 17% -- with no change to the isotope.
Section 6.2 summary.
Checkpoint 6.1. A tracer has \(T_p = 6\) h and \(T_b = 3\) h. Compute \(T_e\) and the cumulated activity for a 500 MBq administration. If a chemist could modify the molecule to clear twice as fast, by what percentage would the dose fall, and would the scan still be feasible?
| Route | Physics | Typical products | Logistics |
|---|---|---|---|
| Reactor | neutron activation or fission | Mo-99, I-131, Xe-133, Lu-177 | centralized; ships parents |
| Cyclotron | charged-particle bombardment | F-18, C-11, N-13, O-15 | on-site or regional |
| Generator | long-lived parent milked for a short-lived daughter | Tc-99m, Ga-68, Rb-82 | at the bedside |
The half-life table of Section 6.2 dictates the choice. O-15 (2 min) and C-11 (20 min) demand a cyclotron and radiochemistry beside the scanner; F-18 (110 min) can be shipped regionally, which is the logistical accident that made FDG dominant.
Molybdenum-99 (\(T_{1/2} = 66\) h) decays to technetium-99m (\(T_{1/2} = 6\) h). This is a coupled two-compartment ODE system, the same structure met in Chapter 1 and used again for tracer kinetics in Section 6.9:
\[\begin{equation} \frac{dN_p}{dt} = -\lambda_p N_p, \qquad \frac{dN_d}{dt} = f\lambda_p N_p - \lambda_d N_d . \tag{6} \end{equation}\]
With no daughter present at \(t=0\), the solution is the Bateman equation
\[\begin{equation} A_d(t) = f\,\frac{\lambda_d}{\lambda_d - \lambda_p}\,A_p(0) \left(e^{-\lambda_p t} - e^{-\lambda_d t}\right), \tag{7} \end{equation}\]
where \(f \approx 0.876\) is the branching fraction of Mo-99 decays yielding Tc-99m. Because \(\lambda_d \gg \lambda_p\), the daughter reaches a maximum at
\[\begin{equation} t_{\max} = \frac{\ln(\lambda_d/\lambda_p)}{\lambda_d - \lambda_p} \approx 23\ \text{h}, \tag{8} \end{equation}\]
after which it declines in step with the parent, transient equilibrium.
lp <- ln2/66; ld <- ln2/6; f_br <- 0.876; Ap0 <- 1000
tt <- seq(0, 96, by = 0.25)
Ap <- Ap0*exp(-lp*tt)
tmax <- log(ld/lp)/(ld - lp)
Ad_noelute <- f_br*(ld/(ld - lp))*Ap0*(exp(-lp*tt) - exp(-ld*tt))
p_g1 <- ggplot(rbind(
data.frame(t = tt, A = Ap, q = "Mo-99 (parent)"),
data.frame(t = tt, A = Ad_noelute, q = "Tc-99m (daughter)")),
aes(t, A, colour = q)) +
geom_line(linewidth = 0.95) +
geom_vline(xintercept = tmax, linetype = "dashed", colour = "grey45") +
annotate("text", x = tmax + 2, y = 250, hjust = 0, size = 2.9, colour = "grey30",
label = sprintf("t_max = %.0f h", tmax)) +
scale_colour_manual(values = bpad_pal[c(1,2)]) +
labs(x = "Time (hours)", y = "Activity (arbitrary units)",
title = "No elution: transient equilibrium")
## Daily elution: reset the daughter to zero every 24 h
Ad <- numeric(length(tt)); last <- 1
for (i in seq_along(tt)) {
tau <- tt[i] - tt[last]
Ad[i] <- f_br*(ld/(ld - lp))*Ap[last]*(exp(-lp*tau) - exp(-ld*tau))
if (i < length(tt) && (tt[i+1] %% 24) < (tt[i] %% 24)) last <- i + 1
}
p_g2 <- ggplot(rbind(
data.frame(t = tt, A = Ap, q = "Mo-99 (parent)"),
data.frame(t = tt, A = Ad, q = "Tc-99m (eluted daily)")),
aes(t, A, colour = q)) +
geom_line(linewidth = 0.95) +
geom_vline(xintercept = c(24, 48, 72), linetype = "dotted", colour = "grey60") +
scale_colour_manual(values = bpad_pal[c(1,2)]) +
labs(x = "Time (hours)", y = "Activity (arbitrary units)",
title = "Daily elution: the clinical routine")
bpad_grid(p_g1, p_g2, ncol = 2)Figure 3: Mo-99/Tc-99m generator dynamics, Eq. (bateman). Left: with no elution the daughter climbs to a maximum near 23 hours, Eq. (tmax), and thereafter decays in step with the parent, which is transient equilibrium. Right: routine practice, in which the generator is eluted daily; each elution strips the accumulated Tc-99m and the daughter rebuilds from zero, with a lower peak each day as the parent decays. The vertical dashed line marks the theoretical optimum elution interval, which is why generators are milked roughly once a day rather than continuously.
yield <- sapply(c(4, 8, 12, 24, 48), function(h)
f_br*(ld/(ld - lp))*(exp(-lp*h) - exp(-ld*h)))
knitr::kable(data.frame(
hours_since_elution = c(4, 8, 12, 24, 48),
daughter_yield_pct = round(100*yield, 1),
fraction_of_max = round(yield/max(Ad_noelute/Ap0), 3)),
col.names = c("Hours since elution","Tc-99m yield (% of parent activity)",
"Fraction of maximum"),
caption = "Generator yield versus time since the last elution. Waiting a full day recovers most of the available daughter; waiting two days recovers little more, because the parent has itself decayed.")| Hours since elution | Tc-99m yield (% of parent activity) | Fraction of maximum |
|---|---|---|
| 4 | 31.7 | 0.460 |
| 8 | 50.4 | 0.731 |
| 12 | 60.9 | 0.883 |
| 24 | 68.9 | 0.999 |
| 48 | 57.8 | 0.839 |
## t_max = ln(lambda_d/lambda_p)/(lambda_d - lambda_p) = 22.8 h
A radiopharmaceutical has two parts: the radionuclide (the beacon, which announces location) and the targeting molecule (the address, which determines where the tracer goes). Because tracers are administered in nanomolar to picomolar amounts, they report on a biological process without perturbing it, the tracer principle, and the reason nuclear medicine can measure receptor occupancy or metabolic rate in a living human.
A good imaging agent must emit photons that escape the body yet stop efficiently in a detector (roughly 100–200 keV for SPECT, 511 keV for PET); have a half-life matched to the biology and short enough to limit dose; avoid particulate emissions, which add dose without adding signal; target its process specifically and remain chemically stable in vivo; and be reliably available.
The dominant PET tracer is [\(^{18}\)F]fluorodeoxyglucose. FDG enters cells through the same GLUT transporters as glucose and is phosphorylated by hexokinase to FDG-6-phosphate. Unlike glucose-6-phosphate, it cannot proceed through glycolysis, so it is trapped in proportion to glucose utilization. That single mechanism makes one molecule useful in oncology (the Warburg effect), neurology (regional metabolism), cardiology (viability), and infection, and it is exactly the irreversibility that makes the Patlak analysis of Section 6.9 valid.
Section 6.3 summary.
Checkpoint 6.2. A generator is eluted at 08:00 and again at 12:00 the same day. Using the yield table, estimate what fraction of the full daily yield the second elution provides, and explain why a department schedules its highest-activity studies first.
A gamma camera records both the position and the energy of every detected photon. The chain is
\[\text{collimator} \rightarrow \text{NaI(Tl) crystal} \rightarrow \text{light guide} \rightarrow \text{PMT array} \rightarrow \text{position and energy logic}.\]
Anger logic computes the position as the PMT-signal-weighted centroid of the light flash. The energy is the total light collected, and a pulse-height analyser accepts only events inside a narrow window about the photopeak (typically \(140 \pm 10\%\) keV for Tc-99m). Energy windowing rejects photons that Compton-scattered in the patient and therefore carry false position information, a strategy that works far better at 140 keV than at 511 keV, for reasons Section 6.5.3 makes quantitative.
A crystal records where a photon landed but not which direction it came from. The collimator, a lead or tungsten slab perforated by fine holes, supplies direction by absorbing everything not travelling nearly parallel to the holes. For a parallel-hole collimator with hole diameter \(d\), effective length \(L\), septal thickness \(t\), and source-to-collimator distance \(b\):
\[\begin{equation} R_{\text{coll}} \approx d\,\frac{L + b}{L}, \qquad g \approx \left(K\,\frac{d}{L}\,\frac{d}{d+t}\right)^{2}, \tag{9} \end{equation}\]
with \(K \approx 0.26\) for hexagonal holes. The system resolution combines collimator and intrinsic detector blur in quadrature:
\[\begin{equation} R_{\text{sys}} = \sqrt{R_{\text{coll}}^{2} + R_{\text{int}}^{2}} . \tag{10} \end{equation}\]
Equation (9) encodes the central compromise of single-photon imaging: resolution improves linearly in \(d\) while sensitivity falls as \(d^{4}\) (through the product of the two \(d\) factors squared). Sensitivity therefore scales roughly as the fourth power of resolution.
Lc <- 24; tc <- 0.20; Kc <- 0.26; R_int <- 3.5 # mm
R_coll <- function(d, b) d*(Lc + b)/Lc
g_coll <- function(d) (Kc*(d/Lc)*(d/(d + tc)))^2
dg <- seq(1.0, 3.5, by = 0.02)
res_df <- do.call(rbind, lapply(c(20, 100, 200), function(b)
data.frame(d = dg, R = R_coll(dg, b),
dist = factor(sprintf("%d mm from collimator", b),
levels = sprintf("%d mm from collimator", c(20,100,200))))))
p_r <- ggplot(res_df, aes(d, R, colour = dist)) +
geom_line(linewidth = 0.9) +
scale_colour_manual(values = bpad_pal[1:3]) +
labs(x = "Hole diameter d (mm)", y = "Geometric resolution (mm)",
title = "Resolution (smaller is sharper)")
p_g <- ggplot(data.frame(d = dg, g = g_coll(dg)), aes(d, g)) +
geom_line(linewidth = 0.95, colour = bpad_pal[2]) +
scale_y_log10() +
labs(x = "Hole diameter d (mm)", y = "Geometric efficiency (log scale)",
title = "Sensitivity (higher is more counts)")
coll_types <- data.frame(
name = c("high resolution", "general purpose", "high sensitivity"),
d = c(1.11, 1.40, 2.54))
coll_types$R <- R_coll(coll_types$d, 100)
coll_types$g <- g_coll(coll_types$d)
p_t <- ggplot(data.frame(R = R_coll(dg, 100), g = g_coll(dg)), aes(R, g)) +
geom_line(linewidth = 0.95, colour = "grey45") +
geom_point(data = coll_types, aes(R, g), size = 2.8, colour = bpad_pal[1]) +
geom_text(data = coll_types, aes(R, g, label = name),
vjust = -1.0, size = 2.7) +
scale_y_log10() +
labs(x = "Geometric resolution at 10 cm (mm)", y = "Efficiency (log scale)",
title = "The trade-off itself")
bpad_grid(p_r, p_g, p_t, ncol = 3)Figure 4: The collimator resolution-sensitivity trade-off, Eq. (collimator). Left: geometric resolution degrades linearly with hole diameter and, critically, with source distance; a source 20 cm from the collimator face is blurred roughly five times more than one at 2 cm, which is why the camera must be kept as close to the patient as anatomy permits. Centre: geometric efficiency on a logarithmic axis, rising steeply with hole diameter. Right: the trade-off plotted directly, with three standard clinical collimators marked; there is no point in the lower-left corner, and every clinical choice is a movement along this curve.
knitr::kable(data.frame(
collimator = coll_types$name,
hole_mm = coll_types$d,
resolution_at_10cm = round(coll_types$R, 1),
system_resolution = round(sqrt(coll_types$R^2 + R_int^2), 1),
efficiency = sprintf("%.2e", coll_types$g),
photons_accepted_pct = sprintf("%.3f%%", 100*coll_types$g),
relative_scan_time = round(max(coll_types$g)/coll_types$g, 1)),
col.names = c("Collimator","Hole (mm)","R_coll at 10 cm (mm)","R_sys (mm)",
"Efficiency","Photons accepted","Relative time for equal counts"),
caption = "Three standard clinical collimators. Moving from high sensitivity to high resolution improves resolution by about a factor of two and costs roughly a factor of five in scan time for the same number of counts.")| Collimator | Hole (mm) | R_coll at 10 cm (mm) | R_sys (mm) | Efficiency | Photons accepted | Relative time for equal counts |
|---|---|---|---|---|---|---|
| high resolution | 1.11 | 5.7 | 6.7 | 1.04e-04 | 0.010% | 6.3 |
| general purpose | 1.40 | 7.2 | 8.0 | 1.76e-04 | 0.018% | 3.7 |
| high sensitivity | 2.54 | 13.1 | 13.6 | 6.51e-04 | 0.065% | 1.0 |
cat(sprintf("A general-purpose collimator accepts %.4f%% of the photons reaching it:\n",
100*g_coll(1.40)))## A general-purpose collimator accepts 0.0176% of the photons reaching it:
cat(sprintf("about %.0f out of every million are used and the rest are absorbed in lead.\n",
1e6*g_coll(1.40)))## about 176 out of every million are used and the rest are absorbed in lead.
The collimator paradox, quantified. The component that makes single-photon imaging possible, by establishing direction, is also the component that makes it hard. A general-purpose collimator transmits about \(2\times10^{-4}\) of the photons that reach it, discarding more than 99.97%. PET dispenses with the physical collimator entirely and recovers two to three orders of magnitude of that loss, which is the single largest reason PET outperforms SPECT on both noise and resolution.
A stationary acquisition gives a planar scintigram: a two-dimensional projection with the same depth ambiguity that motivated CT over radiography in Chapter 5. SPECT rotates one or more camera heads around the patient, collecting projections at many angles and assembling a sinogram identical in structure to CT’s.
SPECT reconstruction is harder than transmission CT for three reasons that will recur throughout this chapter: counts are low so Poisson noise dominates; photons are attenuated on the way out, depth-dependently (Section 6.8); and the collimator’s blur depends on distance from the camera, so the system response varies across the image. Iterative methods (Section 6.7) can model all three; filtered back projection cannot.
Section 6.4 summary.
Checkpoint 6.3. A technologist switches from a general-purpose to a high-resolution collimator. Using the table, state what happens to resolution, to count rate, and to the scan time needed for equal image noise. When is that trade worth making?
A positron travels a short distance, slows, and annihilates, producing two 511 keV photons almost exactly back-to-back (Eq. (3)). If two opposing detectors both register a 511 keV photon within a few nanoseconds, the annihilation must have occurred somewhere on the straight line of response (LOR) joining them.
Direction is thus established by timing rather than by absorbing photons in lead — electronic collimation. This is why PET keeps so much more of its signal than SPECT.
Not every recorded coincidence is real.
For a detector pair with singles rates \(S_1\), \(S_2\) and coincidence window \(2\tau\),
\[\begin{equation} R = 2\tau\,S_1 S_2 \;\propto\; A^{2}, \tag{11} \end{equation}\]
because each singles rate is proportional to activity. Randoms grow quadratically while trues grow linearly, the reason more tracer eventually makes images worse.
Section 6.4.1 said energy windowing rejects scattered photons. That claim deserves scrutiny, because it is far weaker at PET energies than at SPECT energies. The Compton relation from Chapter 5 gives the scattered photon energy:
\[\begin{equation} E' = \frac{E}{1 + \dfrac{E}{m_ec^{2}}(1 - \cos\theta)} . \tag{12} \end{equation}\]
At 511 keV the ratio \(E/m_ec^2\) equals exactly 1, so the energy loss for small-angle scatter is small, and small-angle scatter is precisely the kind that mispositions an LOR without being obviously wrong.
Eprime <- function(E, th_deg) E/(1 + (E/m_e_c2)*(1 - cos(th_deg*pi/180)))
th <- seq(0, 120, by = 0.5)
sc_df <- rbind(
data.frame(theta = th, frac = Eprime(140, th)/140, E0 = "140 keV (Tc-99m)"),
data.frame(theta = th, frac = Eprime(511, th)/511, E0 = "511 keV (PET)"))
p_c1 <- ggplot(sc_df, aes(theta, frac, colour = E0)) +
annotate("rect", xmin = 0, xmax = 120, ymin = 0.85, ymax = 1.0,
fill = bpad_pal[3], alpha = 0.15) +
geom_line(linewidth = 0.95) +
annotate("text", x = 100, y = 0.92, size = 2.9, colour = bpad_pal[3],
label = "+/- 15% window") +
scale_colour_manual(values = bpad_pal[c(1,2)]) +
labs(x = "Scatter angle (degrees)", y = "Fraction of energy retained",
title = "Energy loss on Compton scatter")
max_angle <- function(E, w) {
th_try <- seq(0, 180, by = 0.1)
keep <- Eprime(E, th_try)/E >= (1 - w)
max(th_try[keep])
}
wg <- seq(0.05, 0.40, by = 0.005)
ang_df <- rbind(
data.frame(w = 100*wg, a = sapply(wg, max_angle, E = 140), E0 = "140 keV (Tc-99m)"),
data.frame(w = 100*wg, a = sapply(wg, max_angle, E = 511), E0 = "511 keV (PET)"))
p_c2 <- ggplot(ang_df, aes(w, a, colour = E0)) +
geom_line(linewidth = 0.95) +
geom_vline(xintercept = 15, linetype = "dotted", colour = "grey45") +
scale_colour_manual(values = bpad_pal[c(1,2)]) +
labs(x = "Energy window half-width (%)",
y = "Largest accepted scatter angle (degrees)",
title = "What the window actually lets through")
bpad_grid(p_c1, p_c2, ncol = 2)Figure 5: Why PET scatter rejection is intrinsically weak. Left: fractional energy retained after Compton scatter, Eq. (compton-scatter), for a 140 keV SPECT photon and a 511 keV PET photon; shaded bands show typical acceptance windows. Right: the largest scatter angle that still falls inside the energy window, as a function of window half-width. At 511 keV a plus or minus 15 percent window admits scatter out to roughly 34 degrees, which at a 40 cm field of view can mislocate an event by many centimetres. At 140 keV the same window admits only about 69 degrees but the geometry is far more forgiving because SPECT already collimates. This is why PET requires model-based scatter correction rather than relying on the energy window.
knitr::kable(data.frame(
scatter_angle = c(10, 20, 30, 45, 60, 90),
retained_511 = sprintf("%.1f%%", 100*Eprime(511, c(10,20,30,45,60,90))/511),
inside_15pct_511 = ifelse(Eprime(511, c(10,20,30,45,60,90))/511 >= 0.85, "YES", "no"),
retained_140 = sprintf("%.1f%%", 100*Eprime(140, c(10,20,30,45,60,90))/140),
inside_15pct_140 = ifelse(Eprime(140, c(10,20,30,45,60,90))/140 >= 0.85, "YES", "no")),
col.names = c("Scatter angle (deg)","511 keV: energy retained","Accepted?",
"140 keV: energy retained","Accepted?"),
caption = "Compton energy loss at the two clinically important photon energies. A 30-degree scatter at 511 keV retains 88 percent of its energy and passes a standard window; the same deflection would place the event centimetres from its true position.")| Scatter angle (deg) | 511 keV: energy retained | Accepted? | 140 keV: energy retained | Accepted? |
|---|---|---|---|---|
| 10 | 98.5% | YES | 99.6% | YES |
| 20 | 94.3% | YES | 98.4% | YES |
| 30 | 88.2% | YES | 96.5% | YES |
| 45 | 77.3% | no | 92.6% | YES |
| 60 | 66.7% | no | 88.0% | YES |
| 90 | 50.0% | no | 78.5% | no |
cat(sprintf("At 511 keV a +/-15%% window accepts scatter out to %.0f degrees.\n",
max_angle(511, 0.15)))## At 511 keV a +/-15% window accepts scatter out to 34 degrees.
cat(sprintf("At 140 keV the same window accepts only out to %.0f degrees.\n",
max_angle(140, 0.15)))## At 140 keV the same window accepts only out to 69 degrees.
## Clinical PET scatter fractions of 30-45% in 3D follow directly, and are why
## model-based scatter estimation is mandatory rather than optional.
The competition between trues (\(\propto A\)), randoms (\(\propto A^2\)), scatter, and detector deadtime is summarized by the noise-equivalent count rate:
\[\begin{equation} \mathrm{NECR} = \frac{T^{2}}{T + S + 2R} . \tag{13} \end{equation}\]
NECR is the count rate of an idealized, contamination-free measurement that would give the same SNR as the real data, a single figure of merit for how useful the counts are. The factor 2 on \(R\) accounts for the extra variance introduced by online randoms subtraction.
Detector deadtime must also be modelled. In a paralyzable detector each event extends the dead period, so the live fraction is \(e^{-A/A_d}\) and the observed rate can actually fall at high activity.
A_g <- seq(0.1, 60, by = 0.05)
A_d <- 25
Lfrac <- exp(-A_g/A_d)
kt <- 9.0e3; kr <- 2.2e2; SF <- 0.35
T_ <- kt*A_g*Lfrac^2
R_ <- kr*A_g^2*Lfrac^2
S_ <- SF/(1 - SF)*T_
NECR <- T_^2/(T_ + S_ + 2*R_)
ipk <- which.max(NECR)
rates <- rbind(
data.frame(A = A_g, r = T_, q = "Trues"),
data.frame(A = A_g, r = S_, q = "Scatter"),
data.frame(A = A_g, r = R_, q = "Randoms"),
data.frame(A = A_g, r = NECR, q = "NECR"))
rates$q <- factor(rates$q, levels = c("Trues","Scatter","Randoms","NECR"))
p_n1 <- ggplot(rates, aes(A, r/1e3, colour = q)) +
geom_line(linewidth = 0.95) +
geom_vline(xintercept = A_g[ipk], linetype = "dotted", colour = "grey45") +
annotate("text", x = A_g[ipk] + 1, y = max(T_)/1e3*0.9, hjust = 0, size = 2.9,
colour = "grey30",
label = sprintf("NECR peak\n%.1f kBq/mL", A_g[ipk])) +
scale_colour_manual(values = c(bpad_pal[3], bpad_pal[5], bpad_pal[2], bpad_pal[8])) +
labs(x = "Activity concentration (kBq/mL)", y = "Rate (kcps)",
title = "Count rates and NECR")
p_n2 <- ggplot(data.frame(A = A_g, f = T_/(T_ + S_ + R_)), aes(A, f)) +
geom_line(linewidth = 0.95, colour = bpad_pal[1]) +
geom_vline(xintercept = A_g[ipk], linetype = "dotted", colour = "grey45") +
geom_hline(yintercept = 0.5, linetype = "dashed", colour = "grey55") +
coord_cartesian(ylim = c(0, 1)) +
labs(x = "Activity concentration (kBq/mL)", y = "Fraction of coincidences that are true",
title = "Signal purity falls with activity")
bpad_grid(p_n1, p_n2, ncol = 2)Figure 6: PET count rates versus activity concentration. Left: trues rise linearly at low activity while randoms rise quadratically, Eq. (randoms), and paralyzable deadtime eventually suppresses everything; the noise-equivalent count rate, Eq. (necr), therefore peaks at an optimal activity marked by the dotted line, beyond which injecting more tracer makes the data worse. Right: the fraction of recorded coincidences that are true, which falls steadily with activity; at the NECR peak barely half of the recorded events are usable signal, which is a useful corrective to the intuition that a high count rate means a good scan.
cat(sprintf("NECR peaks at %.1f kBq/mL, where trues = %.1f kcps and randoms = %.1f kcps\n",
A_g[ipk], T_[ipk]/1e3, R_[ipk]/1e3))## NECR peaks at 9.6 kBq/mL, where trues = 40.1 kcps and randoms = 9.4 kcps
cat(sprintf("and only %.0f%% of recorded coincidences are true events.\n",
100*T_[ipk]/(T_[ipk] + S_[ipk] + R_[ipk])))## and only 56% of recorded coincidences are true events.
cat(sprintf("Doubling activity from the peak to %.1f kBq/mL changes NECR by %+.0f%%.\n",
2*A_g[ipk], 100*(NECR[which.min(abs(A_g - 2*A_g[ipk]))]/NECR[ipk] - 1)))## Doubling activity from the peak to 19.2 kBq/mL changes NECR by -25%.
The scintillator determines almost every performance figure of a PET scanner.
p_s1 <- ggplot(scint, aes(decay_ns, atten_len_mm)) +
geom_point(aes(size = light_yield), colour = bpad_pal[1]) +
geom_text(aes(label = crystal), vjust = -1.2, size = 3) +
scale_x_log10() + scale_y_log10() +
scale_size_continuous(range = c(2, 7), name = "light yield") +
theme(legend.position = "right", legend.title = element_text(size = 8)) +
labs(x = "Scintillation decay time (ns, log scale)",
y = "Attenuation length at 511 keV (mm, log scale)",
title = "Fast and dense: the two things that matter")
p_s2 <- ggplot(scint, aes(reorder(crystal, light_yield), light_yield)) +
geom_col(fill = bpad_pal[3]) +
geom_text(aes(label = sprintf("%.0f%% res.", energy_res_pct)),
hjust = -0.15, size = 2.8) +
coord_flip(ylim = c(0, 48)) +
labs(x = NULL, y = "Relative light yield (photons per keV)",
title = "Light yield sets energy resolution")
bpad_grid(p_s1, p_s2, ncol = 2)Figure 7: Scintillator properties and what each one buys. Left: stopping power at 511 keV, expressed as attenuation length, against decay time on logarithmic axes; the ideal crystal sits in the lower-left corner, dense enough to stop 511 keV photons in a short crystal and fast enough for tight coincidence timing. Right: light yield, which governs energy resolution and hence scatter rejection. Lutetium-based crystals combine BGO-like stopping power with a decay time seven times shorter, and it is that combination, not stopping power alone, that made time-of-flight PET clinically possible.
knitr::kable(scint,
col.names = c("Crystal","Density (g/cm3)","Attenuation length at 511 keV (mm)",
"Light yield","Decay time (ns)","Energy resolution (%)"),
caption = "Scintillators used in nuclear medicine. NaI(Tl) dominates SPECT at 140 keV, where its modest density suffices and its high light yield gives excellent energy resolution. PET needs the density of BGO or LSO to stop 511 keV photons, and the 40 ns decay time of LSO to achieve time-of-flight timing.")| Crystal | Density (g/cm3) | Attenuation length at 511 keV (mm) | Light yield | Decay time (ns) | Energy resolution (%) |
|---|---|---|---|---|---|
| NaI(Tl) | 3.67 | 29.1 | 38 | 230.0 | 7.8 |
| BGO | 7.13 | 10.4 | 9 | 300.0 | 12.0 |
| LSO/LYSO | 7.40 | 11.4 | 30 | 40.0 | 10.0 |
| GSO | 6.71 | 14.1 | 8 | 60.0 | 9.0 |
| BaF2 | 4.89 | 20.9 | 2 | 0.8 | 12.0 |
cat(sprintf("BGO decay time %.0f ns vs LSO %.0f ns: a factor of %.1f.\n",
scint$decay_ns[scint$crystal == "BGO"],
scint$decay_ns[scint$crystal == "LSO/LYSO"],
scint$decay_ns[scint$crystal == "BGO"]/scint$decay_ns[scint$crystal == "LSO/LYSO"]))## BGO decay time 300 ns vs LSO 40 ns: a factor of 7.5.
## Coincidence timing resolution scales with the square root of decay time over light
## yield, so LSO wins on both counts. TOF PET is a materials-science achievement.
Section 6.5 summary.
Checkpoint 6.4. A department doubles the injected FDG activity to “get more counts.” Using the NECR figure, state what happens to trues, to randoms, to the true fraction, and to the effective image quality, and identify the one situation in which the change would still be justified.
PET spatial resolution is set by four largely independent contributions that add in quadrature:
\[\begin{equation} R_{\text{PET}} = 1.25\sqrt{\left(\frac{d_{\text{det}}}{2}\right)^{2} + s_{\text{range}}^{2} + \left(0.0022\,D_{\text{ring}}\right)^{2} + b_{\text{decode}}^{2}} , \tag{14} \end{equation}\]
where \(d_{\text{det}}\) is the detector element width, \(s_{\text{range}}\) the effective positron range, \(0.0022D_{\text{ring}}\) the acollinearity term (with \(D_{\text{ring}}\) the ring diameter in mm), \(b_{\text{decode}}\) the block-decoding error, and the factor 1.25 accounts for the reconstruction filter.
Two of these terms are physics, not engineering, and cannot be improved by better detectors.
Positron range. The positron travels a random distance before annihilating, so the detected annihilation point is not the decay point. Range grows with positron endpoint energy: sub-millimetre for F-18 (0.63 MeV) but several millimetres for Rb-82 (3.4 MeV).
Acollinearity. The electron–positron pair is not perfectly at rest, so the photons depart at \(180^\circ \pm 0.25^\circ\). The resulting blur is proportional to the ring diameter, meaning a larger scanner has worse intrinsic resolution, an unusual and counterintuitive scaling.
pet_res <- function(d, s, D, b = 2.2, k = 1.25)
k*sqrt((d/2)^2 + s^2 + (0.0022*D)^2 + b^2)
cfg <- data.frame(
config = c("Clinical, F-18", "Clinical, Ga-68", "Clinical, Rb-82", "Preclinical, F-18"),
d = c(4.0, 4.0, 4.0, 1.6),
s = c(0.54, 1.35, 2.60, 0.54),
D = c(830, 830, 830, 160),
b = c(2.2, 2.2, 2.2, 0.0))
cfg$total <- pet_res(cfg$d, cfg$s, cfg$D, cfg$b)
comp <- do.call(rbind, lapply(seq_len(nrow(cfg)), function(i) data.frame(
config = cfg$config[i],
term = c("detector d/2", "positron range", "acollinearity", "block decoding"),
value = c((cfg$d[i]/2)^2, cfg$s[i]^2, (0.0022*cfg$D[i])^2, cfg$b[i]^2))))
comp$config <- factor(comp$config, levels = cfg$config)
comp$term <- factor(comp$term, levels = c("detector d/2","positron range",
"acollinearity","block decoding"))
p_b1 <- ggplot(comp, aes(config, value, fill = term)) +
geom_col() +
scale_fill_manual(values = bpad_pal[c(1,2,3,5)]) +
coord_flip() +
labs(x = NULL, y = expression("contribution to "*R^2*" (mm"^2*")"),
title = "Where the blur comes from")
Dg <- seq(100, 900, by = 5)
p_b2 <- ggplot(data.frame(D = Dg, a = 0.0022*Dg), aes(D, a)) +
geom_line(linewidth = 0.95, colour = bpad_pal[3]) +
geom_point(data = data.frame(D = c(160, 830), a = 0.0022*c(160, 830)),
size = 2.4, colour = bpad_pal[2]) +
geom_text(data = data.frame(D = c(160, 830), a = 0.0022*c(160, 830)),
aes(label = sprintf("%.2f mm", a)), vjust = -1.0, size = 2.8) +
labs(x = "Ring diameter (mm)", y = "Acollinearity blur (mm)",
title = "Bigger scanners blur more")
dg2 <- seq(0.5, 6, by = 0.02)
iso_df <- do.call(rbind, lapply(list(c("F-18", 0.54), c("Ga-68", 1.35), c("Rb-82", 2.60)),
function(z) data.frame(d = dg2, R = pet_res(dg2, as.numeric(z[2]), 830, 2.2),
iso = z[1])))
p_b3 <- ggplot(iso_df, aes(d, R, colour = iso)) +
geom_line(linewidth = 0.95) +
scale_colour_manual(values = bpad_pal[c(1,5,2)]) +
labs(x = "Detector element width (mm)", y = "Total resolution (mm)",
title = "Each isotope sets a floor")
bpad_grid(p_b1, p_b2, p_b3, ncol = 3)Figure 8: The PET resolution budget, Eq. (pet-resolution). Left: contributions for four configurations, stacked in quadrature; for a clinical F-18 scan the detector width dominates, but for Rb-82 the positron range alone exceeds every engineering term, so no detector improvement can help. Centre: acollinearity blur grows linearly with ring diameter, which is why a small-bore preclinical scanner has intrinsically better resolution than a whole-body one. Right: total resolution against detector element width for three isotopes, showing the floor each isotope imposes; below about 2 mm detector pitch there is nothing left to gain for Rb-82.
knitr::kable(data.frame(
configuration = cfg$config,
detector_mm = cfg$d, positron_range_mm = cfg$s, ring_mm = cfg$D,
acollinearity_mm = round(0.0022*cfg$D, 2),
total_resolution_mm = round(cfg$total, 2)),
col.names = c("Configuration","Detector (mm)","Positron range (mm)","Ring (mm)",
"Acollinearity (mm)","Total FWHM (mm)"),
caption = "Resolution budgets. Rb-82 loses about 1 mm of resolution to positron range alone; a preclinical scanner gains from both small detectors and a small ring.")| Configuration | Detector (mm) | Positron range (mm) | Ring (mm) | Acollinearity (mm) | Total FWHM (mm) |
|---|---|---|---|---|---|
| Clinical, F-18 | 4.0 | 0.54 | 830 | 1.83 | 4.41 |
| Clinical, Ga-68 | 4.0 | 1.35 | 830 | 1.83 | 4.68 |
| Clinical, Rb-82 | 4.0 | 2.60 | 830 | 1.83 | 5.44 |
| Preclinical, F-18 | 1.6 | 0.54 | 160 | 0.35 | 1.28 |
cat(sprintf("Rb-82 versus F-18 on the same scanner: %.2f mm versus %.2f mm (%.0f%% worse)\n",
cfg$total[3], cfg$total[1], 100*(cfg$total[3]/cfg$total[1] - 1)))## Rb-82 versus F-18 on the same scanner: 5.44 mm versus 4.41 mm (23% worse)
## purely because of positron range. No detector upgrade recovers this.
Conventional PET localizes an annihilation only to somewhere on the LOR. If the two photons arrive at slightly different times, the difference locates it along the line:
\[\begin{equation} \Delta x = \frac{c\,\Delta t}{2} . \tag{15} \end{equation}\]
A 400 ps timing resolution gives \(\Delta x \approx 6\) cm, far too coarse to form the image by itself, but enough to confine each event’s contribution to a segment rather than the whole chord. The resulting variance reduction improves SNR by approximately
\[\begin{equation} \text{SNR gain} \approx \sqrt{\frac{D_{\text{object}}}{\Delta x}} , \tag{16} \end{equation}\]
which is larger in bigger patients, precisely where image quality is otherwise worst.
dtg <- seq(50, 800, by = 2)
dxg <- c_light*dtg*1e-12/2*100 # cm
marks <- data.frame(dt = c(600, 400, 215), lab = c("early TOF", "first clinical", "modern"))
marks$dx <- c_light*marks$dt*1e-12/2*100
p_t1 <- ggplot(data.frame(dt = dtg, dx = dxg), aes(dt, dx)) +
geom_line(linewidth = 0.95, colour = bpad_pal[4]) +
geom_point(data = marks, aes(dt, dx), size = 2.4, colour = bpad_pal[2]) +
geom_text(data = marks, aes(dt, dx, label = sprintf("%s\n%.0f ps -> %.1f cm", lab, dt, dx)),
hjust = -0.08, size = 2.6) +
coord_cartesian(xlim = c(50, 950)) +
labs(x = "Coincidence timing resolution (ps)",
y = "Localization along the LOR (cm)",
title = "Timing buys position")
Dg2 <- seq(15, 50, by = 0.5)
gain <- do.call(rbind, lapply(c(600, 400, 215), function(dt) {
dx <- c_light*dt*1e-12/2*100
data.frame(D = Dg2, g = sqrt(Dg2/dx),
timing = factor(sprintf("%d ps", dt), levels = sprintf("%d ps", c(600,400,215))))
}))
p_t2 <- ggplot(gain, aes(D, g, colour = timing)) +
geom_hline(yintercept = 1, linetype = "dotted", colour = "grey55") +
geom_line(linewidth = 0.95) +
scale_colour_manual(values = bpad_pal[c(7,1,2)]) +
labs(x = "Patient diameter (cm)", y = "SNR gain factor",
title = "The gain grows with patient size")
bpad_grid(p_t1, p_t2, ncol = 2)Figure 9: Time-of-flight PET. Left: localization uncertainty along the line of response, Eq. (tof-localization); the labelled points mark historical and current clinical timing resolutions. Right: the SNR gain, Eq. (tof-gain), plotted against patient size for three timing resolutions; the gain grows with patient diameter, which inverts the usual situation in which large patients are hardest to image. Modern 215 ps systems deliver roughly a threefold SNR gain in a 40 cm patient, equivalent to a ninefold increase in scan time or injected activity.
knitr::kable(data.frame(
timing_ps = c(600, 400, 215, 100),
localization_cm = round(c_light*c(600,400,215,100)*1e-12/2*100, 1),
gain_20cm = round(sqrt(20/(c_light*c(600,400,215,100)*1e-12/2*100)), 2),
gain_40cm = round(sqrt(40/(c_light*c(600,400,215,100)*1e-12/2*100)), 2),
equivalent_dose_reduction = sprintf("%.0f%%",
100*(1 - 1/sqrt(40/(c_light*c(600,400,215,100)*1e-12/2*100))^2))),
col.names = c("Timing (ps)","Localization (cm)","SNR gain, 20 cm","SNR gain, 40 cm",
"Equivalent activity saving, 40 cm"),
caption = "Time-of-flight performance. Because SNR scales as the square root of counts, an SNR gain of g is worth a factor g-squared in injected activity or scan time; a modern 215 ps scanner can therefore image a large patient with roughly one twelfth the counts an equivalent non-TOF system would need.")| Timing (ps) | Localization (cm) | SNR gain, 20 cm | SNR gain, 40 cm | Equivalent activity saving, 40 cm |
|---|---|---|---|---|
| 600 | 9.0 | 1.49 | 2.11 | 78% |
| 400 | 6.0 | 1.83 | 2.58 | 85% |
| 215 | 3.2 | 2.49 | 3.52 | 92% |
| 100 | 1.5 | 3.65 | 5.17 | 96% |
Section 6.6 summary.
Checkpoint 6.5. A vendor offers 2 mm detector elements instead of 4 mm on a clinical ring. Using Eq. (14), compute the resolution improvement for F-18 and for Rb-82, and explain why a cardiac department might reasonably decline the upgrade.
SPECT and PET reconstruct the same kind of object as CT, a slice \(f(x,y)\) from projections \(P(\theta,s)\), and the central slice theorem of Chapter 5 applies unchanged. Three features nonetheless make emission reconstruction harder.
Filtered back projection assumes noise-free line integrals. Applied to low-count Poisson data it produces streaks and, worse, negative values, which are physically impossible for a concentration and can turn into negative SUVs.
MLEM models the physics honestly: expected counts in each bin are a linear function of the unknown image, and observed counts are Poisson draws around that expectation. Writing \(x_j\) for image voxel \(j\), \(y_i\) for counts in bin \(i\), and \(a_{ij}\) for the probability that an emission in \(j\) is recorded in \(i\), the log-likelihood is
\[\begin{equation} \ell(x) = \sum_i \left[ y_i \log\!\left(\sum_k a_{ik}x_k\right) - \sum_k a_{ik}x_k \right], \tag{17} \end{equation}\]
and the EM algorithm of Chapter 1 yields the multiplicative update
\[\begin{equation} x_j^{(n+1)} = \frac{x_j^{(n)}}{\sum_i a_{ij}} \sum_i a_{ij}\,\frac{y_i}{\sum_k a_{ik}x_k^{(n)}} . \tag{18} \end{equation}\]
The four steps are: forward-project the current estimate; form the ratio of measured to estimated counts; back-project that ratio; and multiply, normalizing by the sensitivity \(\sum_i a_{ij}\). Because every factor is non-negative, MLEM cannot produce negative concentrations.
OSEM accelerates this by using only a subset of projection angles per update, speeding convergence by roughly the number of subsets, and is the clinical standard.
Students routinely assume more iterations are better. They are not. MLEM converges towards the maximum-likelihood solution, which for noisy data is itself noisy: as iterations proceed, bias falls but variance grows, and total error passes through a minimum. This is precisely the bias–variance decomposition of Chapters 1 and 8, appearing here as an algorithmic stopping rule.
set.seed(1)
N <- 56
xy <- expand.grid(x = 1:N, y = 1:N); cxy <- (N + 1)/2
rr <- sqrt((xy$x - cxy)^2 + (xy$y - cxy)^2)
truth <- matrix(0, N, N)
truth[rr < 23] <- 1
hot1 <- matrix(sqrt((xy$x - 21)^2 + (xy$y - 23)^2) < 4.5, N, N)
hot2 <- matrix(sqrt((xy$x - 36)^2 + (xy$y - 34)^2) < 3.5, N, N)
cold <- matrix(sqrt((xy$x - 35)^2 + (xy$y - 21)^2) < 4.5, N, N)
truth[hot1] <- 4; truth[hot2] <- 6; truth[cold] <- 0.1
rotate <- function(M, deg) {
n <- nrow(M); th <- deg*pi/180; cc <- (n + 1)/2
ix <- matrix(rep(1:n, n), n, n); iy <- matrix(rep(1:n, each = n), n, n)
xs <- ix - cc; ys <- iy - cc
xr <- cos(th)*xs + sin(th)*ys + cc
yr <- -sin(th)*xs + cos(th)*ys + cc
x0 <- floor(xr); y0 <- floor(yr); dx <- xr - x0; dy <- yr - y0
g <- function(a, b) { v <- rep(0, length(a)); ok <- a >= 1 & a <= n & b >= 1 & b <= n
v[ok] <- M[cbind(a[ok], b[ok])]; v }
matrix(g(x0,y0)*(1-dx)*(1-dy) + g(x0+1,y0)*dx*(1-dy) +
g(x0,y0+1)*(1-dx)*dy + g(x0+1,y0+1)*dx*dy, n, n)
}
angles <- seq(0, 175, by = 5)
fwd <- function(M) sapply(angles, function(a) colSums(rotate(M, a)))
back <- function(P) { acc <- matrix(0, N, N)
for (j in seq_along(angles))
acc <- acc + rotate(matrix(rep(P[, j], each = N), N, N), -angles[j])
acc }
sino_clean <- fwd(truth)
lambda_s <- sino_clean*(18/mean(sino_clean[sino_clean > 0]))
counts <- matrix(rpois(length(lambda_s), lambda_s), nrow = nrow(lambda_s))
sens <- back(matrix(1, nrow(counts), ncol(counts))); sens[sens < 1e-6] <- 1e-6
bgmask <- matrix(rr < 20, N, N) & !hot1 & !hot2 & !cold
snaps <- list(); track <- NULL
x <- matrix(1, N, N)
for (it in 1:50) {
proj <- fwd(x); proj[proj < 1e-9] <- 1e-9
x <- x/sens*back(counts/proj)
xs <- x*(sum(truth)/sum(x))
track <- rbind(track, data.frame(
iter = it,
bias = mean(xs[bgmask]) - mean(truth[bgmask]),
noise = sd(xs[bgmask]),
rmse = sqrt(mean((xs - truth)^2)),
hot_recovery = mean(xs[hot1])/mean(truth[hot1])))
if (it %in% c(2, 10, 40)) snaps[[as.character(it)]] <- xs
}
best <- track$iter[which.min(track$rmse)]
gimg <- function(M, ttl) { image(t(M[N:1, ]), col = gpal, axes = FALSE, asp = 1,
main = ttl, cex.main = 0.95); box(col = "grey70") }
op <- par(mfrow = c(2, 3), mar = c(2.2, 2.4, 2.4, 0.8))
gimg(truth, "True tracer map")
image(t(counts[nrow(counts):1, ]), col = gpal, axes = FALSE,
main = "Noisy sinogram (Poisson counts)", cex.main = 0.95); box(col = "grey70")
gimg(snaps[["2"]], "MLEM, 2 iterations")
gimg(snaps[["10"]], "MLEM, 10 iterations")
gimg(snaps[["40"]], "MLEM, 40 iterations (noisy)")
par(mar = c(4.2, 4.2, 2.4, 0.8))
plot(track$iter, track$rmse, type = "l", lwd = 2, col = bpad_pal[1],
xlab = "MLEM iteration", ylab = "RMSE vs truth", main = "Error has a minimum")
abline(v = best, lty = 3, col = bpad_pal[2])
text(best, max(track$rmse)*0.9, sprintf(" min at %d", best), pos = 4, cex = 0.8,
col = bpad_pal[2])Figure 10: MLEM reconstruction and its stopping problem. Top row: the true tracer distribution, the noisy Poisson sinogram actually measured, and the reconstruction after 2, 10, and 40 iterations. Early iterations are smooth but the lesions are under-recovered; late iterations recover contrast fully but amplify noise until the background becomes mottled. Bottom row: quantitatively, background bias falls monotonically toward zero while background noise grows monotonically, so the root-mean-square error passes through a minimum, marked by the dotted line. Clinical protocols stop near that minimum or apply a post-reconstruction filter, which is why an OSEM setting is a clinical parameter and not merely a computational one.
par(op)
knitr::kable(track[track$iter %in% c(1, 2, 5, 10, 20, 30, 40, 50),
c("iter","bias","noise","rmse","hot_recovery")],
row.names = FALSE, digits = 4,
col.names = c("Iteration","Background bias","Background noise","RMSE",
"Hot-lesion recovery"),
caption = "The MLEM bias-variance trade-off. Bias falls and lesion recovery improves monotonically with iteration; background noise grows monotonically; total error is minimized in between. This is the Chapter 1 bias-variance decomposition expressed as a stopping rule.")| Iteration | Background bias | Background noise | RMSE | Hot-lesion recovery |
|---|---|---|---|---|
| 1 | -0.1356 | 0.0951 | 0.7474 | 0.2990 |
| 2 | -0.0156 | 0.1763 | 0.6238 | 0.4346 |
| 5 | 0.0789 | 0.2803 | 0.4133 | 0.7099 |
| 10 | 0.0544 | 0.3458 | 0.3546 | 0.8498 |
| 20 | 0.0186 | 0.4961 | 0.4401 | 0.9036 |
| 30 | 0.0093 | 0.6307 | 0.5375 | 0.9175 |
| 40 | 0.0060 | 0.7415 | 0.6201 | 0.9240 |
| 50 | 0.0042 | 0.8306 | 0.6872 | 0.9279 |
cat(sprintf("bias improves from %+.3f to %+.3f while noise grows from %.3f to %.3f\n",
track$bias[1], track$bias[50], track$noise[1], track$noise[50]))## bias improves from -0.136 to +0.004 while noise grows from 0.095 to 0.831
## RMSE is minimized at iteration 9, not at iteration 50.
cat(sprintf("Hot-lesion recovery reaches %.0f%% by iteration %d but the background\n",
100*track$hot_recovery[best], best))## Hot-lesion recovery reaches 84% by iteration 9 but the background
## noise has already tripled. Every clinical OSEM protocol is a choice on this curve.
Why “run it to convergence” is wrong advice. MLEM converges to the maximum-likelihood image, which fits the noise as well as the signal. Clinical practice therefore stops at a few effective iterations (commonly 2–4 iterations × 8–24 subsets in OSEM) and applies a post-reconstruction Gaussian filter. The consequence for quantification is important: SUV depends on the reconstruction protocol, so serial scans for treatment response must use identical settings. This is precisely the acquisition-shift problem of Chapter 8, and it is why harmonization standards such as EARL exist.
Section 6.7 summary.
Checkpoint 6.6. A physicist increases OSEM from 2 iterations × 8 subsets to 6 × 24. Using the table, predict what happens to small-lesion SUV, to background noise, and to comparability with a prior scan reconstructed at the original setting.
Emitted photons are attenuated escaping the body exactly as in Chapter 5, but now the attenuation corrupts a signal we are trying to quantify rather than the signal we are trying to form. Uncorrected images systematically under-represent the interior, and SUV becomes meaningless.
The geometry differs between the modalities in a way that has an elegant consequence.
SPECT. A detected photon traverses only the tissue between its emission point and the camera, so the surviving fraction \(e^{-\mu(D-a)}\) depends on the emission depth \(a\).
PET. Both annihilation photons must escape, one in each direction:
\[\begin{equation} p_{\text{escape}} = e^{-\mu a}\cdot e^{-\mu(D-a)} = e^{-\mu D}, \qquad \mathrm{ACF} = e^{+\mu D}, \tag{19} \end{equation}\]
which depends only on the total chord length \(D\) and not on where along the LOR the annihilation occurred. This position-independence is what makes PET attenuation correction exact: a single factor applies to the entire LOR, and it can be measured directly from a transmission scan or computed from a CT \(\mu\)-map.
Dbody <- 20
ag <- seq(0, Dbody, by = 0.05)
p_a1 <- ggplot(data.frame(a = ag, s = exp(-mu_140*(Dbody - ag))), aes(a, s)) +
geom_line(linewidth = 0.95, colour = bpad_pal[2]) +
coord_cartesian(ylim = c(0, 1)) +
labs(x = "Emission depth a (cm)", y = "Surviving fraction",
title = "SPECT at 140 keV: depth-dependent")
p_a2 <- ggplot(data.frame(a = ag, s = exp(-mu_511*ag)*exp(-mu_511*(Dbody - ag))),
aes(a, s)) +
geom_line(linewidth = 0.95, colour = bpad_pal[1]) +
geom_hline(yintercept = exp(-mu_511*Dbody), linetype = "dotted", colour = "grey45") +
annotate("text", x = Dbody/2, y = exp(-mu_511*Dbody) + 0.07, size = 2.9,
colour = "grey30",
label = sprintf("constant = %.3f", exp(-mu_511*Dbody))) +
coord_cartesian(ylim = c(0, 1)) +
labs(x = "Emission depth a (cm)", y = "Combined surviving fraction",
title = "PET at 511 keV: position-independent")
Dg3 <- seq(5, 40, by = 0.2)
p_a3 <- ggplot(rbind(
data.frame(D = Dg3, ACF = exp(mu_511*Dg3), q = "PET, 511 keV"),
data.frame(D = Dg3, ACF = exp(mu_140*Dg3), q = "SPECT, 140 keV (max)")),
aes(D, ACF, colour = q)) +
geom_line(linewidth = 0.95) +
scale_y_log10() + scale_colour_manual(values = bpad_pal[c(1,2)]) +
labs(x = "Path length D (cm)", y = "Attenuation correction factor (log scale)",
title = "How large the correction is")
bpad_grid(p_a1, p_a2, p_a3, ncol = 3)Figure 11: The elegant PET attenuation fact. Left: in SPECT the surviving fraction falls exponentially with the remaining depth, so a uniform source appears bright at the surface and dim at the centre, and correction requires knowing where the emission occurred. Centre: in PET the product of the two escape probabilities is exactly constant along the line of response, Eq. (pet-attenuation), so one correction factor serves the whole line. Right: the magnitude of the correction; at 511 keV a 30 cm chord requires multiplying the measured counts by nearly 18, which is why an error in the attenuation map propagates so strongly into SUV.
knitr::kable(data.frame(
chord_cm = c(10, 20, 30, 40),
PET_survival = sprintf("%.3f", exp(-mu_511*c(10,20,30,40))),
PET_ACF = round(exp(mu_511*c(10,20,30,40)), 2),
SPECT_survival_max = sprintf("%.3f", exp(-mu_140*c(10,20,30,40))),
SPECT_ACF_max = round(exp(mu_140*c(10,20,30,40)), 2)),
col.names = c("Path (cm)","PET survival","PET ACF","SPECT survival (deepest)",
"SPECT ACF (deepest)"),
caption = "Attenuation magnitudes. For a 40 cm chord through a large patient the PET correction factor exceeds 45, so a 10 percent error in the attenuation map produces a comparable error in SUV.")| Path (cm) | PET survival | PET ACF | SPECT survival (deepest) | SPECT ACF (deepest) |
|---|---|---|---|---|
| 10 | 0.383 | 2.61 | 0.223 | 4.48 |
| 20 | 0.147 | 6.82 | 0.050 | 20.09 |
| 30 | 0.056 | 17.81 | 0.011 | 90.02 |
| 40 | 0.021 | 46.53 | 0.002 | 403.43 |
cat(sprintf("Through a 20 cm chord only %.1f%% of PET photon pairs escape unattenuated.\n",
100*exp(-mu_511*20)))## Through a 20 cm chord only 14.7% of PET photon pairs escape unattenuated.
cat(sprintf("The correction factor is %.2f, and it is exact only because the product\n",
exp(mu_511*20)))## The correction factor is 6.82, and it is exact only because the product
## of the two escape probabilities does not depend on where the decay happened.
Combining an emission scanner with CT or MR solves two problems at once: it supplies the \(\mu\)-map for attenuation correction, and it supplies the anatomy onto which a functional hot spot is localized.
| System | \(\mu\)-map source | Strengths | Limitations |
|---|---|---|---|
| SPECT/CT | CT, rescaled to 140 keV | attenuation and scatter correction, anatomy | added CT dose |
| PET/CT | CT, rescaled to 511 keV | fast, exact correction, ubiquitous | CT dose; respiratory misregistration |
| PET/MRI | MR tissue-class segmentation | soft-tissue contrast, no CT dose | bone is MR-dark yet highly attenuating |
Because CT is acquired at a much lower effective energy than 511 keV, the Hounsfield units of Chapter 5 must be rescaled to 511 keV attenuation coefficients, typically by a bilinear function of HU, with different slopes for soft tissue and bone because their photoelectric-to-Compton balance differs (Chapter 5, Section 5.2).
Two failure modes of CT-based attenuation correction. First, respiratory misregistration: CT is acquired in seconds and PET over minutes, so a lesion at the lung base can be assigned the \(\mu\) of lung on one and liver on the other, producing a characteristic curvilinear cold artifact. Second, truncation and high-density objects: metal implants and iodinated contrast produce CT values that the bilinear rescaling maps incorrectly, generating spurious hot spots in the corrected PET. Reading the non-attenuation-corrected images alongside the corrected ones is the standard defence.
The elegant PET fact. Because the two annihilation photons together always cross the whole body, PET attenuation correction needs only the total path attenuation, independent of source depth. SPECT enjoys no such simplification, which is one more reason SPECT quantification lags PET.
Section 6.8 summary.
Checkpoint 6.7. Explain why bone is the easiest tissue for PET/CT attenuation correction and the hardest for PET/MRI, and predict the direction of the SUV error if bone is misassigned soft-tissue attenuation.
A static scan gives one snapshot; a dynamic scan gives a time-activity curve (TAC) per region or voxel, whose shape encodes delivery, retention, and washout. Fitting that shape recovers physiological rate constants rather than a single uptake number.
The tracer moves among pools exactly as in the Chapter 1 pharmacokinetic models. The one-tissue model is
\[\begin{equation} \frac{dC_t}{dt} = K_1 C_p(t) - k_2 C_t , \tag{20} \end{equation}\]
and the two-tissue model, standard for FDG, adds a trapped compartment:
\[\begin{equation} \frac{dC_1}{dt} = K_1 C_p(t) - (k_2 + k_3)C_1 + k_4 C_2, \qquad \frac{dC_2}{dt} = k_3 C_1 - k_4 C_2 . \tag{21} \end{equation}\]
Here \(C_1\) is free FDG, \(C_2\) is FDG-6-phosphate, and \(C_p(t)\) is the arterial input function. The rate constants mean delivery (\(K_1\)), washout (\(k_2\)), phosphorylation (\(k_3\)), and dephosphorylation (\(k_4\)). For FDG over a typical scan \(k_4 \approx 0\): trapping is effectively irreversible. The measured signal is \(C_{\text{PET}} = C_1 + C_2\) plus a small blood-volume term.
For an irreversible tracer there is an elegant shortcut that replaces nonlinear ODE fitting with linear regression. Plotting
\[\begin{equation} \frac{C_{\text{PET}}(t)}{C_p(t)} \quad\text{against}\quad \frac{\int_0^t C_p(\tau)\,d\tau}{C_p(t)} \tag{22} \end{equation}\]
gives, at late times, a straight line whose slope is the net influx constant
\[\begin{equation} K_i = \frac{K_1 k_3}{k_2 + k_3}, \tag{23} \end{equation}\]
the quantity that summarizes irreversible trapping and, for FDG, is proportional to the metabolic rate of glucose. The intercept reflects the reversible distribution volume.
Cp_fun <- function(t) 40*t*exp(-1.2*t) + 6*(exp(-0.12*t) - exp(-1.2*t))
simulate_2tc <- function(K1, k2, k3, k4, tmax = 60, dt = 0.02) {
deriv <- function(t, C) { cp <- Cp_fun(t)
c(K1*cp - (k2 + k3)*C[1] + k4*C[2], k3*C[1] - k4*C[2]) }
tt <- seq(0, tmax, by = dt); C <- matrix(0, length(tt), 2)
for (i in 2:length(tt)) {
y <- C[i-1, ]; t0 <- tt[i-1]
a1 <- deriv(t0, y); a2 <- deriv(t0 + dt/2, y + dt/2*a1)
a3 <- deriv(t0 + dt/2, y + dt/2*a2); a4 <- deriv(t0 + dt, y + dt*a3)
C[i, ] <- y + dt/6*(a1 + 2*a2 + 2*a3 + a4)
}
list(t = tt, C1 = C[,1], C2 = C[,2], Ct = C[,1] + C[,2], Cp = Cp_fun(tt), dt = dt)
}
patlak <- function(sim, t_lin = 15) {
intCp <- cumsum((sim$Cp + c(0, head(sim$Cp, -1)))/2)*sim$dt
keep <- sim$t > 2 & sim$Cp > 1e-6
xp <- intCp[keep]/sim$Cp[keep]; yp <- sim$Ct[keep]/sim$Cp[keep]; tp <- sim$t[keep]
fit <- lm(yp[tp > t_lin] ~ xp[tp > t_lin])
list(x = xp, y = yp, t = tp, slope = unname(coef(fit)[2]),
intercept = unname(coef(fit)[1]), r2 = summary(fit)$r.squared)
}
K1 <- 0.10; k2 <- 0.15; k3 <- 0.08
sim0 <- simulate_2tc(K1, k2, k3, 0.0)
Ki_true <- K1*k3/(k2 + k3)
pk0 <- patlak(sim0)
p_k1 <- ggplot(rbind(
data.frame(t = sim0$t, C = sim0$Cp, q = "plasma input Cp"),
data.frame(t = sim0$t, C = sim0$C1, q = "free C1"),
data.frame(t = sim0$t, C = sim0$C2, q = "trapped C2"),
data.frame(t = sim0$t, C = sim0$Ct, q = "measured C1 + C2")),
aes(t, C, colour = q)) +
geom_line(linewidth = 0.9) +
scale_colour_manual(values = bpad_pal[c(2,5,3,1)]) +
labs(x = "Time (min)", y = "Concentration (kBq/mL)",
title = "Input and tissue curves")
p_k2 <- ggplot(data.frame(x = pk0$x, y = pk0$y), aes(x, y)) +
geom_line(linewidth = 0.95, colour = bpad_pal[3]) +
geom_abline(slope = pk0$slope, intercept = pk0$intercept,
linetype = "dashed", colour = bpad_pal[8], linewidth = 0.8) +
labs(x = expression(integral(C[p])/C[p]*" (min)"), y = expression(C[PET]/C[p]),
title = sprintf("Patlak: slope %.4f vs true %.4f", pk0$slope, Ki_true))
rev_df <- do.call(rbind, lapply(c(0, 0.01, 0.03), function(k4v) {
s <- simulate_2tc(K1, k2, k3, k4v); p <- patlak(s)
data.frame(x = p$x, y = p$y, k4 = factor(sprintf("k4 = %.2f", k4v),
levels = sprintf("k4 = %.2f", c(0,0.01,0.03))))
}))
p_k3 <- ggplot(rev_df, aes(x, y, colour = k4)) +
geom_line(linewidth = 0.95) +
scale_colour_manual(values = bpad_pal[c(3,5,2)]) +
labs(x = expression(integral(C[p])/C[p]*" (min)"), y = expression(C[PET]/C[p]),
title = "Reversibility breaks linearity")
bpad_grid(p_k1, p_k2, p_k3, ncol = 3)Figure 12: Two-tissue FDG kinetics and Patlak analysis. Left: the arterial input function and the resulting tissue time-activity curve, decomposed into its free and trapped components; the free compartment tracks the plasma curve and clears with it, while the trapped compartment only ever rises and then holds its accumulated value, which is metabolic trapping made visible. Centre: the Patlak transformation, Eq. (patlak), which becomes linear once the free compartment has equilibrated; the fitted slope recovers the true net influx constant. Right: what happens when trapping is not irreversible; with k4 greater than zero the Patlak plot bends downward and its slope no longer equals K_i, which is why the transformation must not be applied to reversible tracers.
knitr::kable(do.call(rbind, lapply(c(0, 0.005, 0.01, 0.03), function(k4v) {
s <- simulate_2tc(K1, k2, k3, k4v); p <- patlak(s)
data.frame(k4 = k4v, patlak_slope = round(p$slope, 5),
true_Ki = round(Ki_true, 5),
error_pct = sprintf("%+.1f%%", 100*(p$slope/Ki_true - 1)),
r_squared = round(p$r2, 5)) })),
row.names = FALSE,
col.names = c("k4 (1/min)","Patlak slope","True Ki","Error","R-squared"),
caption = "Patlak recovers Ki exactly when trapping is irreversible, and degrades gracefully but measurably as k4 grows. The R-squared remains high even when the slope is biased, so a good linear fit is NOT evidence that the irreversibility assumption holds.")| k4 (1/min) | Patlak slope | True Ki | Error | R-squared |
|---|---|---|---|---|
| 0.000 | 0.03478 | 0.03478 | +0.0% | 1.00000 |
| 0.005 | 0.03026 | 0.03478 | -13.0% | 0.99986 |
| 0.010 | 0.02640 | 0.03478 | -24.1% | 0.99944 |
| 0.030 | 0.01571 | 0.03478 | -54.8% | 0.99500 |
cat(sprintf("Irreversible case: true K_i = %.5f /min, Patlak slope = %.5f /min (R2 = %.5f)\n",
Ki_true, pk0$slope, pk0$r2))## Irreversible case: true K_i = 0.03478 /min, Patlak slope = 0.03478 /min (R2 = 1.00000)
A high \(R^2\) does not validate the model. In the table above, a \(k_4\) of 0.03 min\(^{-1}\) biases the Patlak slope substantially while the linear fit still returns \(R^2\) above 0.99. Goodness of fit measures whether the chosen model describes the data, not whether the right model was chosen, the same distinction Chapter 8 draws between calibration and discrimination. Irreversibility must be justified from the tracer’s chemistry, not inferred from the plot.
Full kinetic modelling requires a dynamic scan and an input function, which are impractical in routine oncology. The standardized uptake value normalizes measured concentration by the concentration that would result if the dose were spread uniformly:
\[\begin{equation} \mathrm{SUV} = \frac{C_{\text{tissue}}\ [\text{kBq mL}^{-1}]} {A_{\text{injected}}\ [\text{kBq}]\,/\,m_{\text{body}}\ [\text{g}]} . \tag{24} \end{equation}\]
A value near 1 means “average”; metabolically active tumours commonly reach 4–15. Variants include SUVmax (hottest voxel: robust to partial-volume loss but noisy), SUVpeak (a small fixed volume: the current recommendation), and SUVmean. Normalizing by lean body mass (SUL) reduces variability in heavier patients, because adipose tissue takes up little FDG yet contributes to body weight.
dose_MBq <- 370; weight_kg <- 70
tissue_conc <- c(tumour = 21.0, liver = 11.0, muscle = 3.0, lung = 1.2)
ref_kBq_per_g <- (dose_MBq*1e3)/(weight_kg*1e3)
SUV <- tissue_conc/ref_kBq_per_g
p_s1 <- ggplot(data.frame(tissue = names(SUV), SUV = as.numeric(SUV)),
aes(reorder(tissue, SUV), SUV)) +
geom_col(fill = bpad_pal[1]) +
geom_hline(yintercept = 1, linetype = "dashed", colour = bpad_pal[2]) +
geom_text(aes(label = sprintf("%.1f", SUV)), hjust = -0.2, size = 3) +
coord_flip(ylim = c(0, 5)) +
labs(x = NULL, y = "SUV (body weight)",
title = sprintf("%d MBq in a %d kg patient", dose_MBq, weight_kg))
wt <- seq(45, 140, by = 1)
lbm <- 1.07*wt - 148*(wt/175)^2 # James formula, male
conc <- 21.0
suv_bw <- conc/((dose_MBq*1e3)/(wt*1e3))
suv_lbm <- conc/((dose_MBq*1e3)/(lbm*1e3))
p_s2 <- ggplot(rbind(
data.frame(w = wt, v = suv_bw, q = "SUV (body weight)"),
data.frame(w = wt, v = suv_lbm, q = "SUL (lean body mass)")),
aes(w, v, colour = q)) +
geom_line(linewidth = 0.95) +
scale_colour_manual(values = bpad_pal[c(2,3)]) +
labs(x = "Patient weight (kg)", y = "Reported uptake value",
title = "Same tumour, same tracer, different number")
bpad_grid(p_s1, p_s2, ncol = 2)Figure 13: SUV and its dependence on things that are not biology. Left: SUV for three tissues at a standard administration. Right: the same tumour concentration expressed as SUV across patient weights, computed by body weight and by lean body mass; because fat is metabolically inert but counts toward body weight, the body-weight SUV rises spuriously in heavier patients while the lean-mass-normalized SUL stays nearly flat. This is why SUL is preferred for comparing patients or tracking a patient whose weight changes during therapy.
cat(sprintf("Reference concentration: %d MBq / %d kg = %.2f kBq/g\n",
dose_MBq, weight_kg, ref_kBq_per_g))## Reference concentration: 370 MBq / 70 kg = 5.29 kBq/g
cat(sprintf("A 21 kBq/mL tumour reads SUV %.1f at 60 kg and SUV %.1f at 120 kg:\n",
conc/((dose_MBq*1e3)/(60*1e3)), conc/((dose_MBq*1e3)/(120*1e3))))## A 21 kBq/mL tumour reads SUV 3.4 at 60 kg and SUV 6.8 at 120 kg:
cat(sprintf("a %.0f%% apparent increase with no change in the tumour whatever.\n",
100*(conc/((dose_MBq*1e3)/(120*1e3))/(conc/((dose_MBq*1e3)/(60*1e3))) - 1)))## a 100% apparent increase with no change in the tumour whatever.
cat(sprintf("Using lean body mass the same comparison changes by only %.0f%%.\n",
100*(conc/((dose_MBq*1e3)/((1.07*120-148*(120/175)^2)*1e3))/
(conc/((dose_MBq*1e3)/((1.07*60-148*(60/175)^2)*1e3))) - 1)))## Using lean body mass the same comparison changes by only 26%.
Section 6.9 summary.
Checkpoint 6.8. A patient’s SUVmax rises from 6.0 to 7.2 between two scans while the tumour is unchanged, and the patient has lost 12 kg. Explain the direction of the artifact and state which measurement would have avoided it.
The single largest quantitative error in clinical PET is not noise, attenuation, or scatter: it is blur. When a structure is comparable in size to the scanner’s resolution, its activity is smeared over neighbouring voxels, so small hot lesions read cold. The severity is captured by the recovery coefficient, the ratio of measured to true concentration.
For a uniform sphere of diameter \(d\) convolved with an isotropic Gaussian point spread function of full width at half maximum \(f\) (so \(\sigma = f/2.355\)), the value recovered at the sphere centre is
\[\begin{equation} \mathrm{RC}(d) = \mathrm{erf}\!\left(\frac{d}{2\sqrt{2}\,\sigma}\right) - \sqrt{\frac{2}{\pi}}\,\frac{d}{2\sigma}\, \exp\!\left(-\frac{d^{2}}{8\sigma^{2}}\right). \tag{25} \end{equation}\]
RC <- function(d, fwhm) {
s <- fwhm/(2*sqrt(2*log(2)))
2*pnorm(d/(2*s)) - 1 - sqrt(2/pi)*(d/(2*s))*exp(-d^2/(8*s^2))
}
dg <- seq(1, 40, by = 0.1)
rc_df <- do.call(rbind, lapply(c(4, 6, 10), function(f)
data.frame(d = dg, rc = RC(dg, f),
fwhm = factor(sprintf("FWHM %d mm", f),
levels = sprintf("FWHM %d mm", c(4,6,10))))))
p_p1 <- ggplot(rc_df, aes(d, rc, colour = fwhm)) +
geom_hline(yintercept = c(0.5, 0.9), linetype = "dotted", colour = "grey55") +
geom_line(linewidth = 0.95) +
scale_colour_manual(values = bpad_pal[c(3,1,2)]) +
coord_cartesian(ylim = c(0, 1.02)) +
labs(x = "Lesion diameter (mm)", y = "Recovery coefficient",
title = "How much signal survives blur")
true_suv <- 8
p_p2 <- ggplot(subset(rc_df, fwhm == "FWHM 6 mm"), aes(d, rc*true_suv)) +
geom_hline(yintercept = true_suv, linetype = "dashed", colour = bpad_pal[2]) +
geom_line(linewidth = 0.95, colour = bpad_pal[1]) +
annotate("text", x = 30, y = true_suv + 0.5, size = 2.9, colour = bpad_pal[2],
label = "true SUV = 8") +
coord_cartesian(xlim = c(0, 40), ylim = c(0, 9)) +
labs(x = "Lesion diameter (mm)", y = "Apparent SUV",
title = "What a 6 mm scanner reports")
p_p3 <- ggplot(subset(rc_df, fwhm == "FWHM 6 mm" & d >= 4), aes(d, 1/rc)) +
geom_line(linewidth = 0.95, colour = bpad_pal[4]) +
scale_y_log10() +
labs(x = "Lesion diameter (mm)", y = "Required correction factor (log scale)",
title = "Correction becomes unstable")
bpad_grid(p_p1, p_p2, p_p3, ncol = 3)Figure 14: Partial-volume loss, quantified by Eq. (recovery-coefficient). Left: recovery coefficient against lesion diameter for three scanner resolutions; a lesion whose diameter equals the resolution FWHM recovers less than a third of its true concentration, and accurate quantification requires diameters of at least two to three FWHM, marked by the dotted lines. Centre: the same result expressed as the apparent SUV of a lesion whose true SUV is 8, showing that a 6 mm lesion on a 6 mm scanner reports SUV 2.3 rather than 8. Right: the correction factor that must be applied, which exceeds three below one FWHM and becomes numerically unstable, which is why partial-volume correction is applied cautiously and lesion size is always reported alongside SUV.
knitr::kable(data.frame(
diameter_mm = c(4, 6, 8, 10, 12, 15, 20, 30),
d_over_fwhm = round(c(4,6,8,10,12,15,20,30)/6, 2),
recovery = round(RC(c(4,6,8,10,12,15,20,30), 6), 3),
apparent_SUV = round(RC(c(4,6,8,10,12,15,20,30), 6)*8, 2),
underestimate = sprintf("%.0f%%", 100*(1 - RC(c(4,6,8,10,12,15,20,30), 6)))),
col.names = c("Diameter (mm)","d / FWHM","Recovery coefficient",
"Apparent SUV (true 8)","Underestimate"),
caption = "Partial-volume loss on a 6 mm FWHM scanner. A 10 mm lesion still loses more than a quarter of its true SUV, which is why a rising SUV between scans can reflect a growing lesion rather than a more active one.")| Diameter (mm) | d / FWHM | Recovery coefficient | Apparent SUV (true 8) | Underestimate |
|---|---|---|---|---|
| 4 | 0.67 | 0.107 | 0.86 | 89% |
| 6 | 1.00 | 0.291 | 2.33 | 71% |
| 8 | 1.33 | 0.518 | 4.15 | 48% |
| 10 | 1.67 | 0.722 | 5.78 | 28% |
| 12 | 2.00 | 0.864 | 6.91 | 14% |
| 15 | 2.50 | 0.966 | 7.73 | 3% |
| 20 | 3.33 | 0.998 | 7.99 | 0% |
| 30 | 5.00 | 1.000 | 8.00 | 0% |
cat(sprintf("A lesion of diameter equal to the FWHM recovers only %.0f%% of its true value.\n",
100*RC(6, 6)))## A lesion of diameter equal to the FWHM recovers only 29% of its true value.
cat(sprintf("Accurate quantification needs d >= %.0f mm on a 6 mm scanner (RC > 0.9).\n",
min(dg[RC(dg, 6) > 0.9])))## Accurate quantification needs d >= 13 mm on a 6 mm scanner (RC > 0.9).
Why this matters more than noise. A 7 mm lung nodule on a 6 mm FWHM scanner recovers about 40% of its true SUV. If the true SUV is 6, clearly malignant, the report will read 2.4, which is often called benign. Conversely, a lesion that grows from 7 mm to 14 mm with no change in metabolic activity will show its SUV roughly double, which can be misread as progression of aggressiveness rather than of size. Lesion diameter must always be reported alongside SUV, and comparisons across scans are only valid when the lesion is large relative to the resolution.
Section 6.10 summary.
Checkpoint 6.9. Two lesions in the same patient have identical true SUV of 9; one is 8 mm and the other 25 mm. Using the table, predict the reported SUVs on a 6 mm scanner and explain what a clinician would wrongly conclude about their relative aggressiveness.
Chapter 5 measured dose from an external beam that stops when the tube is switched off. Here the source is inside the patient and keeps emitting until it decays or is excreted. The dose is therefore determined by biology as much as by physics, and the governing quantity is the cumulated activity of Eq. (5).
The Medical Internal Radiation Dose (MIRD) schema computes the mean absorbed dose to a target organ \(r_T\) from activity residing in a source organ \(r_S\):
\[\begin{equation} D(r_T) = \sum_{r_S} \tilde A(r_S)\; S(r_T \leftarrow r_S), \tag{26} \end{equation}\]
where \(S(r_T\leftarrow r_S)\), the S-value, is the absorbed dose in the target per unit cumulated activity in the source, tabulated for standard phantoms. The physics is entirely inside \(S\); the biology is entirely inside \(\tilde A\). Effective dose then follows the Chapter 5 definitions,
\[\begin{equation} E_{\text{eff}} = \sum_T w_T \sum_R w_R D_{T,R} \;\approx\; \Gamma_{\text{tracer}} \times A_{\text{administered}}, \tag{27} \end{equation}\]
where the practical form on the right uses a tabulated effective dose coefficient \(\Gamma\) in mSv MBq\(^{-1}\), the number a clinical physicist actually uses.
Why particulate emissions are excluded from imaging agents. In Eq. (26) the S-value counts all energy deposited, but only photons that escape contribute to the image. A beta particle deposits its entire energy within a millimetre or two of the decay, contributing to \(D\) and nothing to the picture. This is precisely why a pure gamma emitter such as Tc-99m is an excellent imaging agent and why I-131, which emits both, is a mediocre imaging agent and an excellent therapeutic one, the same equation read in two directions.
bkgd_yr <- 3.0
dc <- dose_coeff[order(dose_coeff$mSv), ]
dc$study <- factor(dc$study, levels = dc$study)
p_d1 <- ggplot(dc, aes(study, mSv)) +
geom_col(fill = bpad_pal[1]) +
geom_hline(yintercept = bkgd_yr, linetype = "dashed", colour = bpad_pal[2]) +
geom_text(aes(label = sprintf("%.1f", mSv)), hjust = -0.2, size = 2.8) +
annotate("text", x = 2, y = bkgd_yr*2.4, size = 2.8, colour = bpad_pal[2],
label = "annual background") +
coord_flip(ylim = c(0, 22)) +
labs(x = NULL, y = "Effective dose (mSv)",
title = "Typical nuclear medicine studies")
comp_dose <- data.frame(
study = c("Chest radiograph", "Tc-99m bone scan", "FDG PET (tracer only)",
"Head CT", "Chest CT", "FDG PET/CT (low-dose CT)",
"Abdomen/pelvis CT", "FDG PET/CT (diagnostic CT)"),
mSv = c(0.02, 4.2, 7.0, 2.0, 7.0, 7.0 + 4.0, 10.0, 7.0 + 10.0),
kind = c("X-ray","nuclear","nuclear","CT","CT","hybrid","CT","hybrid"))
comp_dose$study <- factor(comp_dose$study, levels = comp_dose$study[order(comp_dose$mSv)])
p_d2 <- ggplot(comp_dose, aes(study, mSv, fill = kind)) +
geom_col() +
geom_text(aes(label = sprintf("%.1f", mSv)), hjust = -0.2, size = 2.8) +
scale_fill_manual(values = bpad_pal[c(3,1,4,2)]) +
coord_flip(ylim = c(0, 22)) +
labs(x = NULL, y = "Effective dose (mSv)",
title = "Nuclear medicine against Chapter 5")
bpad_grid(p_d1, p_d2, ncol = 2)Figure 15: Nuclear medicine dose in context. Left: effective doses for common studies, computed as the product of the tabulated coefficient and the typical administered activity, Eq. (effective-dose-nm); the dashed line marks annual natural background. Right: the same studies compared with the CT examinations of Chapter 5. The essential and often surprising point is that for a PET/CT the diagnostic CT component usually delivers more dose than the radiotracer, so dose reduction efforts should begin with the CT protocol rather than the injected activity.
knitr::kable(data.frame(
study = as.character(dc$study),
coefficient = dc$mSv_per_MBq,
activity_MBq = dc$typical_MBq,
effective_mSv = round(dc$mSv, 2),
background_days = round(dc$mSv/(bkgd_yr/365)),
chest_xray_equivalents = round(dc$mSv/0.02)),
col.names = c("Study","Coefficient (mSv/MBq)","Activity (MBq)","Effective dose (mSv)",
"Background days","Chest radiographs"),
caption = "Effective doses from administered activity. Coefficients are tabulated per unit activity, so dose scales linearly with what is injected; halving the administered activity halves the dose, and a time-of-flight scanner or a longer scan can often make that possible.")| Study | Coefficient (mSv/MBq) | Activity (MBq) | Effective dose (mSv) | Background days | Chest radiographs |
|---|---|---|---|---|---|
| Rb-82 (cardiac PET) | 0.0013 | 1480 | 1.92 | 234 | 96 |
| Tc-99m MAG3 (renal) | 0.0070 | 370 | 2.59 | 315 | 130 |
| Ga-68 DOTATATE | 0.0210 | 150 | 3.15 | 383 | 158 |
| Tc-99m MDP (bone) | 0.0057 | 740 | 4.22 | 513 | 211 |
| I-123 ioflupane (DaTscan) | 0.0240 | 185 | 4.44 | 540 | 222 |
| FDG PET | 0.0190 | 370 | 7.03 | 855 | 352 |
| Tc-99m sestamibi (rest + stress) | 0.0079 | 1110 | 8.77 | 1067 | 438 |
| Tl-201 (cardiac) | 0.1400 | 111 | 15.54 | 1891 | 777 |
## FDG PET tracer alone: 7.0 mSv. A diagnostic CT adds about 10 mSv,
cat(sprintf("so the CT is %.0f%% of a %.0f mSv PET/CT examination.\n",
100*10/(0.019*370 + 10), 0.019*370 + 10))## so the CT is 59% of a 17 mSv PET/CT examination.
## Dose optimization in hybrid imaging should therefore start with the CT protocol.
Because the patient is the source, three considerations follow that have no analogue in CT. Patients remain mildly radioactive after the study, so brief contact precautions apply with infants and pregnant contacts. Pregnancy and lactation require specific handling: many tracers cross the placenta or are excreted in milk, and breastfeeding interruption times are tracer-specific. And hydration and voiding genuinely reduce dose, because they shorten the biological half-life in Eq. (4), a rare case in which patient behaviour directly alters the physics.
Therapy inverts every design goal. For imaging we want photons that escape and no particles; for therapy we want particles that deposit locally and just enough photon emission to verify delivery. Lu-177 (beta, with imageable gammas) and I-131 (beta plus a 364 keV gamma) are chosen precisely for that combination, and alpha emitters such as Ac-225 deposit enormous energy within a few cell diameters.
emit <- data.frame(
agent = c("Tc-99m (gamma)", "F-18 (positron)", "I-131 (beta)", "Lu-177 (beta)",
"Y-90 (beta)", "Ac-225 (alpha)"),
range_mm = c(50, 0.6, 0.8, 0.67, 3.6, 0.05),
LET = c(0.2, 0.3, 0.3, 0.3, 0.3, 80),
role = c("imaging","imaging","therapy + imaging","therapy + imaging",
"therapy","therapy"))
# ggplot(emit, aes(range_mm, LET, colour = role)) +
# geom_point(size = 3.5) +
# geom_text(aes(label = agent), vjust = -1.1, size = 2.9, show.legend = FALSE) +
# scale_x_log10() + scale_y_log10() +
# scale_colour_manual(values = bpad_pal[c(1,3,2)]) +
# coord_cartesian(ylim = c(0.1, 300)) +
# labs(x = "Typical range in tissue (mm, log scale)",
# y = expression("Linear energy transfer (keV/"*mu*"m, log scale)"),
# title = "Imaging agents and therapy agents occupy opposite corners")
library(ggplot2)
library(ggrepel)
# Assuming bpad_pal is already defined in your environment
ggplot(emit, aes(range_mm, LET, colour = role)) +
geom_point(size = 3.5) +
geom_text_repel(
aes(label = agent),
size = 2.9,
show.legend = FALSE,
box.padding = 0.4, # Padding around the text box
point.padding = 0.3, # Padding around the data point
force = 2 # Repulsion force between overlapping labels
) +
scale_x_log10() +
scale_y_log10() +
scale_colour_manual(values = bpad_pal[c(1, 3, 2)]) +
coord_cartesian(ylim = c(0.1, 300)) +
labs(
x = "Typical range in tissue (mm, log scale)",
y = expression("Linear energy transfer (keV/" * mu * "m, log scale)"),
title = "Imaging agents and therapy agents occupy opposite corners"
)Figure 16: Why therapy and imaging want opposite things. Emission particle range in tissue is plotted against the linear energy transfer that determines biological effectiveness, on logarithmic axes. Imaging agents must sit in the lower-left region, depositing as little energy as possible so that dose is minimized while photons escape to form the image. Therapeutic agents must sit toward the upper right, where beta particles cross tens of cell diameters and alpha particles deposit their entire energy within a few. The same molecular targeting vector can carry either payload, which is the central idea of theranostics.
## Ac-225 alpha particles deposit their energy within about 50 micrometres, roughly
## five cell diameters, which is why alpha therapy can spare tissue immediately
## adjacent to a targeted micrometastasis.
Four chapters, four safety physics. Chapter 3 limits cavitation and heating through MI and TI. Chapter 4 limits RF heating through SAR and manages a permanent projectile hazard. Chapter 5 limits external ionizing dose, which stops when the tube stops. This chapter manages internal ionizing dose, which continues until the tracer decays or clears, and is therefore the only modality in the book where the patient’s hydration, renal function, and behaviour after the scan change the delivered dose.
Section 6.11 summary.
Checkpoint 6.10. A 370 MBq FDG PET/CT is performed with a diagnostic-quality CT. Compute the tracer dose, estimate the total, and state which component you would reduce first and by what mechanism.
FDG-PET/CT is used to stage cancer, restage after treatment, assess response — a metabolic drop typically precedes shrinkage by weeks, and detect recurrence in anatomically distorted post-surgical tissue. Because FDG marks glucose metabolism rather than malignancy, infection, inflammation, brown fat, and healing tissue also light up, which is why FDG is read as PET/CT and never alone.
Myocardial perfusion imaging compares flow at stress and rest: reversible defects indicate ischaemia, fixed defects indicate infarction. Viability imaging asks whether dysfunctional muscle is still alive: a perfusion–metabolism mismatch (poor flow, preserved FDG) marks hibernating myocardium that may recover after revascularization, whereas a matched defect marks scar. Rb-82 and N-13 ammonia additionally permit absolute flow quantification in mL min\(^{-1}\) g\(^{-1}\).
FDG-PET maps regional glucose metabolism, with characteristic hypometabolic patterns separating Alzheimer’s disease from frontotemporal and Lewy-body dementias. Amyloid PET detects \(\beta\)-amyloid plaques and tau PET images neurofibrillary tangles, whose distribution tracks cognitive decline more closely. Dopamine-transporter SPECT images presynaptic terminals, distinguishing Parkinsonian syndromes from essential tremor. The trajectory is clear: from imaging where energy is spent, to imaging specific proteins.
The same targeting logic that delivers a beacon can deliver a therapeutic payload. Theranostics pairs a diagnostic tracer with a therapeutic radionuclide against the same target: Ga-68 DOTATATE imaging confirms somatostatin-receptor expression before Lu-177 DOTATATE therapy; Ga-68 or F-18 PSMA imaging precedes Lu-177 PSMA therapy in prostate cancer. Imaging selects the patients in whom therapy will localize, which is individualized dosimetry in the strictest sense, and the reason Eq. (26) is computed patient-by-patient in therapy rather than from a standard phantom.
Clinical example. A patient with metastatic neuroendocrine tumour undergoes Ga-68 DOTATATE PET/CT. Lesions with SUV above 20 confirm high receptor density; a lesion that is FDG-avid but DOTATATE-negative indicates dedifferentiation and would not respond to Lu-177 therapy. The two scans together stratify the patient in a way neither could alone, the molecular analogue of the multiparametric approach seen for MRI in Chapter 4.
From the clinical question to the acquisition.
| Question | Study | Physical property measured | Binding limit |
|---|---|---|---|
| Is this mass malignant? | FDG PET/CT | glucose metabolism | partial volume below ~10 mm; inflammation false positives |
| Has treatment worked? | FDG PET/CT, matched protocol | change in metabolism | SUV depends on reconstruction and weight |
| Is the myocardium viable? | perfusion + FDG | flow–metabolism mismatch | requires two acquisitions |
| What is absolute myocardial flow? | Rb-82 or N-13 PET | tracer kinetics | Rb-82 positron range degrades resolution |
| Is there amyloid pathology? | amyloid PET | plaque binding | binary read; does not diagnose dementia alone |
| Is this Parkinsonian? | DaTscan SPECT | presynaptic transporter density | SPECT resolution; long uptake time |
| Where is the bone metastasis? | Tc-99m MDP SPECT/CT | osteoblastic activity | non-specific; degenerative uptake |
| Will radionuclide therapy localize? | Ga-68 DOTATATE or PSMA PET | receptor expression | must precede therapy |
| Could this be answered without radiation? | MRI (Ch. 4) or ultrasound (Ch. 3) | relaxation; acoustic impedance | availability; no molecular specificity |
Goal. Reproduce the count-rate model of Section 6.5, locate the NECR peak, and show that the optimum shifts with the coincidence window.
necr_curve <- function(A, tau_ns, kt = 9.0e3, kr0 = 2.2e2, SF = 0.35, A_d = 25) {
L <- exp(-A/A_d)
Tr <- kt*A*L^2
Rr <- kr0*(tau_ns/6)*A^2*L^2 # randoms scale with the window width
Sr <- SF/(1 - SF)*Tr
Tr^2/(Tr + Sr + 2*Rr)
}
Ag <- seq(0.1, 60, by = 0.05)
lab_a <- do.call(rbind, lapply(c(2, 4, 6), function(w)
data.frame(A = Ag, N = necr_curve(Ag, w),
win = factor(sprintf("%d ns window", w),
levels = sprintf("%d ns window", c(2,4,6))))))
p_la1 <- ggplot(lab_a, aes(A, N/1e3, colour = win)) +
geom_line(linewidth = 0.95) +
scale_colour_manual(values = bpad_pal[c(3,1,2)]) +
labs(x = "Activity concentration (kBq/mL)", y = "NECR (kcps)",
title = "A narrower window is worth activity")
wgrid <- seq(1, 8, by = 0.1)
opt_A <- sapply(wgrid, function(w) Ag[which.max(necr_curve(Ag, w))])
p_la2 <- ggplot(data.frame(w = wgrid, A = opt_A), aes(w, A)) +
geom_line(linewidth = 0.95, colour = bpad_pal[4]) +
labs(x = "Coincidence window (ns)", y = "Optimal activity (kBq/mL)",
title = "Where the peak sits")
bpad_grid(p_la1, p_la2, ncol = 2)Figure 17: Lab A. Left: noise-equivalent count rate for three coincidence timing windows; a narrower window admits fewer randoms, so the NECR peak both rises and moves to higher activity, which is a second and less obvious benefit of fast detectors. Right: the optimal activity concentration as a function of the timing window, showing that halving the window roughly doubles the activity a scanner can usefully accept.
knitr::kable(data.frame(
window_ns = c(2, 4, 6),
optimal_A = sapply(c(2,4,6), function(w) Ag[which.max(necr_curve(Ag, w))]),
peak_NECR_kcps = round(sapply(c(2,4,6), function(w) max(necr_curve(Ag, w)))/1e3, 1)),
col.names = c("Coincidence window (ns)","Optimal activity (kBq/mL)","Peak NECR (kcps)"),
caption = "Narrowing the coincidence window reduces randoms, raising both the peak NECR and the activity at which it occurs.")| Coincidence window (ns) | Optimal activity (kBq/mL) | Peak NECR (kcps) |
|---|---|---|
| 2 | 11.20 | 23.9 |
| 4 | 10.25 | 21.7 |
| 6 | 9.60 | 20.0 |
Try it yourself. Set the scatter fraction SF to 0.5, typical of a large patient in 3D
mode, and observe how much the peak NECR falls even though trues are unchanged. Then set the
deadtime scale A_d to 12 to model a slower detector and note that the optimum moves to
lower activity, the reason BGO scanners were operated at lower injected doses than
modern LSO systems.
The full MLEM demonstration appears in Section 6.7. Use that code as the starting point.
Try it yourself.
angles into 4 subsets and updating once per subset.
Verify that 3 OSEM iterations approximately match 12 MLEM iterations.Goal. Connect the acquisition to the reported number: simulate the two-tissue model, recover \(K_i\) by Patlak regression, and show how much of a reported SUV is biology and how much is protocol.
K1 <- 0.10; k2 <- 0.15; k3 <- 0.08
sim <- simulate_2tc(K1, k2, k3, 0)
Ki_true <- K1*k3/(k2 + k3)
starts <- seq(2, 40, by = 1)
Ki_hat <- sapply(starts, function(s) patlak(sim, t_lin = s)$slope)
p_lc1 <- ggplot(data.frame(s = starts, Ki = Ki_hat), aes(s, Ki)) +
geom_hline(yintercept = Ki_true, linetype = "dashed", colour = bpad_pal[2]) +
geom_line(linewidth = 0.95, colour = bpad_pal[1]) +
geom_vline(xintercept = 15, linetype = "dotted", colour = "grey45") +
annotate("text", x = 30, y = Ki_true*1.04, size = 2.9, colour = bpad_pal[2],
label = "true Ki") +
labs(x = "Patlak fit start time (min)", y = expression(K[i]*" (1/min)"),
title = "When to start the fit")
RC <- function(d, fwhm) { s <- fwhm/(2*sqrt(2*log(2)))
2*pnorm(d/(2*s)) - 1 - sqrt(2/pi)*(d/(2*s))*exp(-d^2/(8*s^2)) }
true_conc <- 21.0; base_dose <- 370; base_wt <- 70; base_d <- 12; base_fwhm <- 6
suv_of <- function(conc, dose, wt, d, fwhm)
RC(d, fwhm)*conc/((dose*1e3)/(wt*1e3))
scen <- rbind(
data.frame(factor = "uptake time 45 vs 90 min",
low = suv_of(true_conc*0.82, base_dose, base_wt, base_d, base_fwhm),
high = suv_of(true_conc*1.10, base_dose, base_wt, base_d, base_fwhm)),
data.frame(factor = "patient weight 55 vs 110 kg",
low = suv_of(true_conc, base_dose, 55, base_d, base_fwhm),
high = suv_of(true_conc, base_dose, 110, base_d, base_fwhm)),
data.frame(factor = "lesion 8 vs 20 mm",
low = suv_of(true_conc, base_dose, base_wt, 8, base_fwhm),
high = suv_of(true_conc, base_dose, base_wt, 20, base_fwhm)))
suv_true <- true_conc/((base_dose*1e3)/(base_wt*1e3))
p_lc2 <- ggplot(scen, aes(y = factor)) +
geom_vline(xintercept = suv_true, linetype = "dashed", colour = bpad_pal[2]) +
geom_segment(aes(x = low, xend = high, yend = factor),
linewidth = 2.4, colour = bpad_pal[1], alpha = 0.75) +
geom_point(aes(x = low), size = 2.4, colour = bpad_pal[8]) +
geom_point(aes(x = high), size = 2.4, colour = bpad_pal[8]) +
annotate("text", x = suv_true, y = 3.45, size = 2.8, colour = bpad_pal[2],
label = sprintf("true SUV %.1f", suv_true)) +
labs(x = "Reported SUV", y = NULL,
title = "How much of a reported SUV is protocol?")
bpad_grid(p_lc1, p_lc2, ncol = 2)Figure 18: Lab C. Left: net influx constant recovered by Patlak regression as the analysis start time is varied; too early a start includes the pre-equilibrium period and biases the slope upward, while any start after about 15 minutes is stable, which is the basis of standard clinical protocols. Right: the reported SUV of a fixed 12 mm lesion as three protocol choices vary independently, with the true value marked; uptake time, patient weight, and lesion size each move the reported number by tens of percent without any change in the underlying biology.
cat(sprintf("True K_i = %.5f /min; Patlak from 15 min = %.5f /min (error %+.2f%%)\n",
Ki_true, patlak(sim, 15)$slope, 100*(patlak(sim, 15)$slope/Ki_true - 1)))## True K_i = 0.03478 /min; Patlak from 15 min = 0.03478 /min (error +0.01%)
cat(sprintf("Fitting from 2 min instead gives %.5f /min (error %+.1f%%).\n",
patlak(sim, 2)$slope, 100*(patlak(sim, 2)$slope/Ki_true - 1)))## Fitting from 2 min instead gives 0.03479 /min (error +0.0%).
cat(sprintf("\nA true SUV of %.1f is reported anywhere from %.1f to %.1f depending on\n",
suv_true, min(scen$low), max(scen$high)))##
## A true SUV of 4.0 is reported anywhere from 2.1 to 5.4 depending on
## uptake time, patient weight, and lesion size alone. This is why response
## assessment requires a fixed protocol, not merely a fixed scanner.
Nuclear medicine inverts the geometry of every other imaging chapter in this book. The source is inside the patient, the image is a map of an injected molecule rather than of tissue structure, and contrast comes from biology. That inversion buys a molecular sensitivity, picomolar, that no other clinical modality approaches, and it pays for that sensitivity in every other currency.
Three themes recur.
Everything is count-limited. Emission images are built from \(10^5\)–\(10^6\) photons against CT’s \(10^9\), so \(\mathrm{SNR}=\sqrt{\bar n}\) governs the whole discipline. It explains why the collimator that makes SPECT possible also throws away 99.98% of the signal; why PET’s electronic collimation was such a decisive advance; why NECR peaks and then falls; why MLEM must be stopped early; and why time-of-flight, which merely narrows where an event can have occurred, is worth a factor of ten in effective counts.
Quantification is fragile, and the fragility is physical. A reported SUV depends on lesion size through partial-volume recovery, on the reconstruction protocol through the bias–variance trade-off, on body composition through the normalization, on uptake time through the kinetics, and on the attenuation map through a correction factor that can exceed 40. A lesion of diameter equal to the scanner’s FWHM reports 29% of its true value. None of these is a measurement error in the ordinary sense; each is a physical effect that must be either corrected or reported. This is the same lesson Chapter 8 draws about biomarkers, met here in its most concrete form.
Design goals invert between imaging and therapy. An imaging agent should emit photons that escape and nothing else; a therapy agent should deposit particles locally and emit just enough photons to verify delivery. The MIRD equation reads the same in both directions — only the desired answer changes. Theranostics exploits exactly this by putting the two payloads on one targeting vector, and it is why individualized dosimetry has become a clinical necessity rather than a research exercise.
The field is moving quickly. Total-body PET scanners with 2 m axial fields of view increase sensitivity by more than an order of magnitude, enabling either ten-fold dose reduction or dynamic whole-body kinetic imaging that was previously impossible. Solid-state photodetectors are pushing coincidence timing below 200 ps, and sub-100 ps timing would begin to make PET genuinely direct rather than tomographic. New tracers, PSMA, FAPI, tau, continue the shift from imaging metabolism to imaging specific molecules. And deep-learning reconstruction offers further count reduction with the hallucination risks that Chapter 8 treats at length.
| Term | Meaning |
|---|---|
| Acollinearity | Departure from exactly \(180^\circ\) between annihilation photons; blur \(\propto\) ring diameter |
| Activity | Decays per second; becquerel (1 Bq \(=\) 1 s\(^{-1}\)), curie (1 Ci \(=3.7\times10^{10}\) Bq) |
| ALARA | As Low As Reasonably Achievable; the dose-optimization principle |
| Anger logic | PMT-weighted centroid that localizes each scintillation |
| Annihilation | Positron–electron conversion into two 511 keV photons |
| Bateman equation | Solution for a decaying parent feeding a decaying daughter |
| Coincidence window | Timing interval \(2\tau\) within which two photons are called a coincidence |
| Collimator | Lead or tungsten holes establishing photon direction in SPECT |
| Cumulated activity | \(\tilde A = 1.443A_0T_e\); total decays, proportional to absorbed dose |
| DaTscan | Dopamine-transporter SPECT imaging of presynaptic terminals |
| Effective dose coefficient | mSv per MBq administered, tabulated per tracer |
| Effective half-life | \(1/T_e = 1/T_p + 1/T_b\); always shorter than either component |
| Electronic collimation | Direction from coincidence timing rather than lead |
| FDG | [\(^{18}\)F]fluorodeoxyglucose, trapped in proportion to glucose use |
| Line of response | The line joining two detectors registering a coincidence |
| Metabolic trapping | Phosphorylated FDG cannot proceed through glycolysis |
| MIRD | Formalism \(D = \sum \tilde A\,S\) separating biology from physics |
| MLEM / OSEM | Poisson-likelihood iterative reconstruction; OSEM uses ordered subsets |
| NECR | \(T^2/(T+S+2R)\); effective count rate accounting for contamination |
| Partial-volume effect | Loss of measured signal for structures near the resolution limit |
| Patlak plot | Linearization of irreversible uptake whose slope is \(K_i\) |
| Positron range | Distance travelled before annihilation; an isotope-dependent resolution floor |
| Randoms | Unrelated photons within the timing window; rate \(\propto A^2\) |
| Recovery coefficient | Ratio of measured to true concentration for a small object |
| S-value | Absorbed dose per unit cumulated activity, from a source to a target organ |
| Scatter fraction | \(S/(T+S)\); 30–45% in clinical 3D PET |
| SUV / SUL | Standardized uptake value; lean-body-mass normalized variant |
| Theranostics | Pairing a diagnostic tracer with a therapeutic radionuclide on one vector |
| Time-of-flight | Using arrival-time difference to localize along the LOR |
| Tracer principle | Measuring a process with quantities too small to perturb it |
| Transient equilibrium | Daughter activity tracking a longer-lived parent |
A Tc-99m dose is calibrated at 740 MBq. (a) How much activity remains after 12 h from physical decay alone (\(T_p = 6.0\) h)? (b) If the biological half-life in an organ is 8.0 h, what is the effective half-life? (c) Compute the cumulated activity in that organ.
A tracer with \(\Gamma = 0.019\) mSv/MBq is administered at 370 MBq. (a) Compute the effective dose. (b) Express it in days of natural background (3 mSv/yr). (c) If a new formulation halves the biological half-life while \(T_p\) is unchanged at 1.83 h and \(T_b\) was 4 h, compute the new \(T_e\) and the fractional dose reduction.
For Mo-99 (\(T_{1/2}=66\) h) feeding Tc-99m (\(T_{1/2}=6\) h) with \(f = 0.876\): (a) compute \(\lambda_p\) and \(\lambda_d\); (b) compute \(t_{\max}\); (c) compute the daughter yield as a fraction of parent activity 24 h after elution.
A parallel-hole collimator has \(L = 24\) mm, \(t = 0.2\) mm, \(K = 0.26\). (a) Compute \(R_{\text{coll}}\) at 10 cm for \(d = 1.4\) mm. (b) Compute the geometric efficiency. (c) Repeat for \(d = 2.8\) mm and state the factor change in each. (d) By what factor does scan time change for equal counts?
A detector pair has \(S_1 = S_2 = 8\times10^5\) s\(^{-1}\) and \(2\tau = 6\) ns. (a) Compute \(R\). (b) If activity doubles, by what factor do randoms and trues each change? (c) Explain from Eq. (13) why image quality eventually falls.
A scanner has 4 mm detectors, an 830 mm ring, and 2.2 mm block decoding. Compute the total resolution for (a) F-18 (\(s = 0.54\) mm) and (b) Rb-82 (\(s = 2.60\) mm). (c) If detectors were reduced to 2 mm, recompute both. (d) Comment on which upgrade is worth buying.
A scanner has 215 ps timing resolution. (a) Compute the localization \(\Delta x\). (b) Compute the SNR gain for a 35 cm patient. (c) By what factor could the injected activity be reduced at constant image quality?
A body presents a 24 cm path at 511 keV with \(\mu = 0.095\) cm\(^{-1}\). (a) Show the combined survival is the same for annihilations 4 cm and 12 cm deep. (b) Compute the ACF. (c) If the \(\mu\)-map is 10% too low, what is the fractional SUV error?
For \(K_1 = 0.09\), \(k_2 = 0.12\), \(k_3 = 0.06\) min\(^{-1}\), \(k_4 = 0\): (a) compute \(K_i\). (b) What do the Patlak slope and intercept represent? (c) Why does \(k_4 > 0\) break the linearity, and in which direction does the slope err?
A tumour concentration of 18 kBq/mL is measured 60 min after 350 MBq in an 80 kg patient. (a) Compute the body-weight SUV. (b) Using James lean body mass \(\mathrm{LBM} = 1.07w - 148(w/175)^2\) for a male, compute SUL. (c) Explain the difference.
On a 6 mm FWHM scanner (\(\sigma = f/2.355\)): (a) compute the recovery coefficient for an 8 mm lesion. (b) If the reported SUV is 3.2, estimate the true SUV. (c) State two reasons not to apply this correction routinely.
A region records 400 counts. (a) What is the relative noise? (b) How many counts are needed to halve it? (c) By what factor must scan time increase? (d) What does that do to the dose?
An FDG PET (370 MBq, \(\Gamma = 0.019\)) is performed with a diagnostic CT delivering 10 mSv. (a) Compute the tracer dose and the total. (b) What percentage is the CT? (c) Name two ways to reduce each component and their costs.
A cardiology unit must choose between Rb-82 (\(T_{1/2}=76\) s, \(s=2.6\) mm, generator) and N-13 ammonia (\(T_{1/2}=10\) min, \(s=0.7\) mm, cyclotron). (a) Compare resolution on the same scanner. (b) Compare logistics. (c) Which would you choose for a centre without a cyclotron, and what is the cost?
Ga-68 DOTATATE PET shows lesions with SUV 25. (a) Why is this scan performed before Lu-177 therapy? (b) Why is Lu-177 preferred to a pure beta emitter such as Y-90 when post-therapy imaging is desired? (c) Why is patient-specific dosimetry used in therapy but not in diagnosis?
| Problem | Computed answer |
|---|---|
| 1(a) | 185 MBq |
| 1(b) | 3.43 h |
| 1(c) | 3661 MBq h |
| 2(a) | 7.03 mSv |
| 2(b) | 855 days |
| 2(c) | T_e: 1.26 -> 0.96 h, dose falls 24% |
| 3(a) | 0.01050 and 0.1155 per h |
| 3(b) | 22.8 h |
| 3(c) | 68.9% of parent activity |
| 4(a) | 7.2 mm |
| 4(b) | 1.76e-04 |
| 4(c) | R = 14.5 mm (x2.0), g = 8.02e-04 (x4.6) |
| 4(d) | time falls by x4.6 |
| 5(a) | 3.84e+03 per s |
| 5(b) | randoms x4, trues x2 |
| 6(a) | 450.6 keV |
| 6(b) | yes: retains 88.2% (> 85%) |
| 6(c) | 135.0 keV, retains 96.5% (also inside) |
| 7(a) | 4.41 mm |
| 7(b) | 5.44 mm |
| 7(c) | F-18 3.85 mm, Rb-82 4.99 mm |
| 8(a) | 3.2 cm |
| 8(b) | 3.30 |
| 8(c) | x10.9 |
| 9(b) | 9.78 |
| 9(c) | SUV low by 20% |
| 10(a) | 0.0300 /min |
| 11(a) | 4.11 |
| 11(b) | 2.81 (LBM = 54.7 kg) |
| 12(a) | 0.518 |
| 12(b) | 6.2 |
| 13(a) | 5% |
| 13(b) | 1600 counts |
| 13(c) | x4 time, x4 dose |
| 14(a) | tracer 7.0 mSv, total 17.0 mSv |
| 14(b) | 59% |
| 15(a) | Rb-82 5.44 mm vs N-13 4.45 mm |