Source code for pytomography.io.PET.petsird
from __future__ import annotations
import pytomography
import torch
from . import prd
from pytomography.metadata import PETTOFMeta
from typing import Sequence
[docs]def get_detector_ids(
petsird_file: str,
read_tof: bool | None = None,
read_energy: bool | None = None,
time_block_ids: Sequence[int] | None = None,
return_header : bool = False
) -> tuple[prd.types.Header, torch.Tensor]:
"""Read all time blocks of a PETSIRD listmode file
Parameters
----------
petsird_file : str
the PETSIRD listmode file
read_tof : bool | None, optional
read the TOF bin information of every event
default None means that is is auto determined
based on the scanner information (length of tof bin edges)
read_energy : bool | None, optional
read the energy information of every event
default None means that is is auto determined
based on the scanner information (length of energy bin edges)
Returns
-------
tuple[prd.types.Header, torch.Tensor]
PRD listmode file header, 2D array containing all event attributes
"""
with prd.BinaryPrdExperimentReader(petsird_file) as reader:
# Read header and build lookup table
header = reader.read_header()
# bool that decides whether the scanner has TOF and whether it is
# meaningful to read TOF
if read_tof is None:
r_tof: bool = len(header.scanner.tof_bin_edges) > 1
else:
r_tof = read_tof
# bool that decides whether the scanner has energy and whether it is
# meaningful to read energy
if read_energy is None:
r_energy: bool = len(header.scanner.energy_bin_edges) > 1
else:
r_energy = read_energy
# loop over all time blocks and read all meaningful event attributes
event_attribute_list = []
for time_block in reader.read_time_blocks():
if (time_block_ids is None) or time_block.id in time_block_ids:
if r_tof and r_energy:
event_attribute_list += [
[
e.detector_1_id,
e.detector_2_id,
e.tof_idx,
e.energy_1_idx,
e.energy_2_idx,
]
for e in time_block.prompt_events
]
elif r_tof and (not r_energy):
event_attribute_list += [
[
e.detector_1_id,
e.detector_2_id,
e.tof_idx,
]
for e in time_block.prompt_events
]
elif (not r_tof) and r_energy:
event_attribute_list += [
[
e.detector_1_id,
e.detector_2_id,
e.energy_1_idx,
e.energy_2_idx,
]
for e in time_block.prompt_events
]
else:
event_attribute_list += [
[
e.detector_1_id,
e.detector_2_id,
]
for e in time_block.prompt_events
]
detector_ids = torch.tensor(event_attribute_list).cpu()
if return_header:
return detector_ids, header
else:
return detector_ids
[docs]def get_scanner_LUT_from_header(header: prd.Header) -> torch.Tensor:
"""Obtains the scanner lookup table (relating detector IDs to physical coordinates) from a PETSIRD header.
Args:
header (prd.Header): PETSIRD header
Returns:
torch.Tensor: scanner lookup table.
"""
x_pos = [det.x for det in header.scanner.detectors]
y_pos = [det.y for det in header.scanner.detectors]
z_pos = [det.z for det in header.scanner.detectors]
scanner_LUT = torch.vstack([
torch.tensor(x_pos),
torch.tensor(y_pos),
torch.tensor(z_pos)
]).T.cpu()
return scanner_LUT
[docs]def get_TOF_meta_from_header(header: prd.Header, n_sigmas: float = 3.) -> PETTOFMeta:
"""Obtain time of flight metadata from a PETSIRD header
Args:
header (prd.Header): PETSIRD header
n_sigmas (float, optional): Number of sigmas to consider when performing TOF projection. Defaults to 3..
Returns:
PETTOFMeta: Time of flight metadata.
"""
num_tof_bins = header.scanner.tof_bin_edges.shape[0] - 1
tofbin_width = (header.scanner.tof_bin_edges[1] - header.scanner.tof_bin_edges[0])
fwhm_tof = header.scanner.tof_resolution
return PETTOFMeta(num_tof_bins, tofbin_width, fwhm_tof, n_sigmas)