Scanner definition
In YRT-PET, a scanner is defined in two parts: the scanner parameters file (JSON format) and the Look-Up-Table (LUT) file in binary format.
Scanner parameters
The following parameters in the JSON file define the scanner:
VERSION: Scanner file format version. The current version is3.2. [Mandatory]scannerName: Scanner name. This is value is not used in the reconstruction. [Mandatory]detCoord: Path to the LUT file. The path is relative to the JSON file’s parent folder.If this field is unspecified, a LUT will be generated from the scanner’s properties.
axialFOV: Axial Field-of-View (mm). [Mandatory]crystalSize_trans: Crystal size in the transaxial dimension (mm). [Mandatory]crystalSize_z: Crystal size in the axial dimension (mm). [Mandatory]crystalDepth: Crystal size in the direction of its orientation (mm). [Mandatory]scannerRadius: Scanner radius (mm). [Mandatory].This represents the distance between detector blocks and the isocenter.
detsPerRing: Number of detectors per ring. [Mandatory]numRings: Number of rings. [Mandatory]numDOI: Number of DOI layers. [Mandatory]detsPerBlock: Number of detectors per block in the transaxial dimension.This property is ignored if a LUT is provided
The following properties are necessary to define sensitivity images and histogram shapes:
maxRingDiff: Maximum ring difference two detectors must have to define a valid LOR. [Mandatory]minAngDiff: Minimum difference two detectors must have to define a valid LOR. Has to be an even number. [Mandatory]
The following properties are used only in scatter estimation:
collimatorRadius: Collimator radius (mm). Only used in scatter estimation.fwhm: Energy resolution FWHM (keV). Only used in scatter estimation.energyLLD: Energy Low-Level-Discriminant (keV). Only used in Scatter estimation.
An optional field can be used to mask detectors:
detMask: Path to mask file. The mask file stores one boolean per detector (in the same order as the LUT file):trueif the detector is acive,falseif it is masked. The file is store in binary format, without header, and with one byte per detector.
Example
Here’s an example of a Scanner’s JSON file
{
"VERSION": 3.2,
"scannerName": "myscanner",
"detCoord": "myscanner.lut",
"axialFOV": 250.0,
"crystalSize_z": 1.0,
"crystalSize_trans": 1.0,
"crystalDepth": 8.0,
"scannerRadius": 130.0,
"detsPerRing": 800,
"numRings": 150,
"numDOI": 2,
"maxRingDiff": 50,
"minAngDiff": 230,
"detsPerBlock": 40,
"fwhm": 0.2,
"energyLLD": 400,
"collimatorRadius": 100
}
Look-Up-Table
The LUT is a binary file that defines all the detecting elements, which can either be individual crystals, or, in the case of scanners with Depth-Of-Interation (DOI), detection positions.
The number of detection elements in the LUT must match the following:
detsPerRing * numRings * numDOI
For each element, the LUT defines, in 32-bit float:
X, Y, Z center position of the element
X, Y, Z orientation (unit vector) of the element, pointing towards the exterior of the scanner
X Position of detector 0 (float32)
Y Position of detector 0 (float32)
Z Position of detector 0 (float32)
X Orientation of detector 0 (float32)
Y Orientation of detector 0 (float32)
Z Orientation of detector 0 (float32)
X Position of detector 1 (float32)
Y Position of detector 1 (float32)
Z Position of detector 1 (float32)
X Orientation of detector 1 (float32)
Y Orientation of detector 1 (float32)
Z Orientation of detector 1 (float32)
...
The LUT’s elements should be ordered in the following way:
Position in the ring in either clockwise or anti-clockwise
Ring position, either ascending or descending order
DOI position, from inner to outer layers.
For Python users
Due to the simplicity of this format, it can be read using the following lines:
import numpy as np
lut = np.fromfile("<myscanner>.lut", dtype=np.float32).reshape((-1, 6))
Then, one can use matplotlib to display the scanner’s detector positions:
#<>
import matplotlib.pyplot as plt
N = 800 # Example: The number of crystals per ring is 800
plt.scatter(lut[:N, 0], lut[:N, 1])
Or the scanner’s detector orientations:
#<>
plt.plot(lut[:N, 3]) # X orientation
plt.plot(lut[:N, 4]) # Y orientation
Here, N is the number of detectors in a ring.
To display the scanner in three dimensions, the repository
plot_scanner
can do that using VTK.
One can also generate the LUT using Python and save it using:
lut.tofile("<mynewscanner>.lut")
Note: The code above assumes the data type of lut to be np.float32.
For plugin developers
The value returned by the functions getDetector1, getDetector2,
and getDetectorPair should correspond to the index of the detector(s) in that
LUT.