[gate-users] ASCII File format
Schmidtlein, Ross/Sloan-Kettering Institute
schmidtr at mskcc.org
Thu May 26 15:45:05 CEST 2005
Hi Graeme,
I wrote one for the basic Michelogram. The latex is below.
ross
\appendix
\subsection{Rebinning GATE Coincidences}
\label{sec:Sinogram_builder}
GATE uses the Crystal, Block, and Module identification numbers to
organize detection events into sinograms. This nomenclature is
comparable to that used in actual scanners. In this scanner, the events
are identified and stored by subdividing the scanner into 36 Crystals
per Block, 6 Blocks per Module (in a two by three Block configuration),
and 56 Modules for the complete gantry.
In this case, the GATE crystal, block, and module ID's for each
coincidence pair are converted into crystal-ring pairs via the following
equations\footnotemark[1]\footnotetext[1]{$INT$ and $MOD$ functions take
the integer and modulus of their arguments, respectively.},
\begin{eqnarray}
Ring_{1,2} = INT\left(\frac{CrystalID_{1,2}}{6}\right) \nonumber \\
+ 6 \times INT\left(\frac{BlockID_{1,2}}{2}\right) ,
\end{eqnarray}
\noindent and,
\begin{eqnarray} Crystal_{1,2} = MOD\left(CrystalID_{1,2},6\right)
\nonumber \\
+ 6 \times \left[MOD\left(BlockID_{1,2},2\right) \right. \nonumber \\
\left. + 2 \times ModuleID_{1,2}\right].
\label{eq:ring_num}
\end{eqnarray}
\noindent The subscripts 1,2 correspond to each of the detected events
that make up the coincidence pair. This indexing used by GATE is shown
in Figure~\ref{fig:singles_binning}.
\begin{figure}
\includegraphics [scale=.22,angle=0]{./singles_binning.eps}
\footnotesize{\caption{The indexing scheme used by GATE to define
detector locations within a Module. Note that the scanner has 56
Modules, each containing 6 Blocks, which in turn contain 36 Crystals
each.}\label{fig:singles_binning}}
\end{figure}
\subsection{Building Sinograms}
The crystal-ring pairs represent lines of response (LOR) that are
rearranged (binned) into a Michelogram format (developed by Christian
Michel~\cite{3DPET:1998}). In this format, the LOR's are binned into a
block matrix with indice dimensions in the $\phi$, \emph{r}, and
$\theta$ directions, where $\phi$ = azimuthal, \emph{r} = radial, and
$\theta$ = ring difference bins.
The azimuthal binning of the LOR's is achieved by noting that for each
projection plane the sum of the crystal numbers (C\#) for a coincidence
pair will always sum to one of two numbers that are unique to a
particular projection plane. These two numbers are offset from one
another by the total number of crystals in the scanner's ring (i.e. in
the GE Advance/Discovery LS scanner $N=672$, thus \emph{sum} = C\# and
C\# + 672). The index \emph{i} for a given $\phi$ can then be expressed
as,
\begin{equation}
i = \left[ \left( Crystal_{1} + Crystal_{2} \right) + Const. \right],
\end{equation}
\noindent where the \emph{Const.} is an arbitrary offset which satisfies
the boundary condition $\phi = 0$. In the case of interlacing, $i \in
[0,N/2)$, and $\phi_i$ is therefore given by,
\begin{equation}
\phi_i = \frac{2 \pi i}{N}.
\end{equation}
Radial binning is done by observing that the radial distance from the
center of the scanner is related to the index difference of the crystals
in the pair. In terms of indexing, the index $j$ for $r$ can be
expressed as,
\begin{equation}
\left| j \right| = N/2 - \left| Crystal_{1} - Crystal_{2} \right|,
\end{equation}
\noindent with $j \in [0,N/2)$ for the case of interlacing. The sign of
\emph{j} is determined by the position of the LOR relative to $\phi_i$:
\begin{equation} j = \left\{ \begin{array}{ll}
+\left|j\right| & \mbox{if } crystal_{1,2} \in \left[i, i + N/2 \right]
\\
-\left|j\right| & {otherwise}\end{array}
\right. .
\end{equation}
\noindent Another way of stating this is that an LOR for a crystal pair
in the right-half plane, with respect to the LOR's projection plane,
will have a positive index, whereas the left-half plane will have a
negative index. Thus for a scanner of radius $R$, this results in the
radial distance $r_j$ being expressed by,
\begin{equation}
r_j = R \sin \left[\pi \left( \frac{j + 1/2}{N} \right) \right] .
\end{equation}
Lastly, the ring difference, or the index of the axial angle $\theta$,
is simply given by the ring difference as determined above in
equation~\ref{eq:ring_num}. Here coincidence pairs with $\theta > 0$ are
stored as $(ring_1, ring_2)$ while those with $\theta < 0$ are stored as
$(ring_2, ring_1)$. They are assigned in the following manner,
\begin{equation}
(ring_1, ring_2) ~\forall~ crystal_1 \in \left[i - N/4, i + N/4 \right]
,
\end{equation}
\noindent and
\begin{equation}
(ring_2, ring_1) ~\forall~ crystal_2 \in \left[i - N/4, i + N/4 \right]
.
\end{equation}
\noindent The crystal that lies in the lower-half plane with respect to
the $i^{th}$ projection plane will define the first index.
In practice, the coincidence events described in this section are
interlaced from $\phi$ into $r$. This has the effect of halving the
number of $\phi$ indices and doubling the number of $r$ indices; $i \in
[0,N) \rightarrow [0,N/2)$, and $j \in [0,N/4) \rightarrow [0,N/2)$.
Figure~\ref{fig:sinogram_binning} illustrates the indexing strategy used
to convert the detector and ring number pairs into sinogram counts.
\begin{figure}
\includegraphics [scale=.37,angle=0]{./sinogram_binning.eps}
\footnotesize{\caption{A simple example that shows the sinogram indexing
for two projection planes of a scanner with 16 detectors that interlaces
with $\phi$ into $r$.}\label{fig:sinogram_binning}}
\end{figure}
-----Original Message-----
From: gate-users-bounces at lphe1pet1.epfl.ch
[mailto:gate-users-bounces at lphe1pet1.epfl.ch] On Behalf Of Graeme
O'Keefe
Sent: Wednesday, May 25, 2005 7:25 PM
To: GATE feedback and helpline for Users
Subject: Re: [gate-users] ASCII File format
Hi Jan,
I am writing a sorter at the moment to convert gate ASCII output to
Philips UGM format sinograms.
I have the sinogram storage sorted out but a small problem with the
(xPosA, xPosB) mapping to (rho, phi).
Maybe someone has done it already, if not, it may be of use to some
people out there, I'll let you know when I've 'sorted' it out.
I had the same problem with the ASCII output for cylindrical PET, here
is what I found.
In GateCoincidenceDigi.cc, you will find:
# GateCoicidenceDigi.cc
#std::ofstream& operator<<(std::ofstream& flux, GateCoincidenceDigi*
digi)
#{
# GatePulse pulse;
# for (G4int iP=0; iP<2; iP++) {
# pulse = digi->GetPulse(iP);
# flux
#1 << " " << std::setw(7) << pulse.GetRunID()
#2 << " " << std::setw(7) << pulse.GetEventID()
#3 << " " << std::setw(5) << pulse.GetSourceID()
#4 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(3) <<
pulse.GetSourcePosition().x()/mm
#5 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(3) <<
pulse.GetSourcePosition().y()/mm
#6 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(3) <<
pulse.GetSourcePosition().z()/mm
#7 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(23) <<
pulse.GetTime()/s
#8 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(3) <<
pulse.GetEnergy()/MeV
#9 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(3) <<
pulse.GetGlobalPos().x()/mm
#10 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(3) <<
pulse.GetGlobalPos().y()/mm
#11 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(3) <<
pulse.GetGlobalPos().z()/mm
#12-17 << " " << std::setw(5) << pulse.GetOutputVolumeID()
#18 << " " << std::setw(5) << pulse.GetNPhantomCompton()
#19 << " " << std::setw(5) << pulse.GetNCrystalCompton()
#20 << " " << std::setw(5) << pulse.GetNPhantomRayleigh()
#21 << " " << std::setw(5) << pulse.GetNCrystalRayleigh()
#22 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(3) <<
pulse.GetScannerPos().z()/mm
#23 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setprecision(3) <<
pulse.GetScannerRotAngle()/deg;
# }
So for two pulses for a coin => 46 columns, the extra two columns per
pulse being from the Rayleigh events.
Four columns additional to documentation from Rayleigh scattering,
columns 20, 21, 43, 44
Likewise, the singles ASCII format is different from the documented
singles ASCII format.
In GateSingleDigi.cc, you will find:
#GateSingleDigi.cc
#std::ofstream& operator<<(std::ofstream& flux, GateSingleDigi* digi)
#{ flux
#1 << " " << std::setw(7) << digi->GetRunID()
#2 << " " << std::setw(7) << digi->GetEventID()
#3 << " " << std::setw(5) << digi->GetSourceID()
#4 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setw(10) <<
std::setprecision(3) << digi->GetSourcePosition().x()/mm
#5 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setw(10) <<
std::setprecision(3) << digi->GetSourcePosition().y()/mm
#6 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setw(10) <<
std::setprecision(3) << digi->GetSourcePosition().z()/mm
#7-12 << " " << std::setw(5) << digi->GetOutputVolumeID()
#13 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setw(30) <<
std::setprecision(23) << digi->GetTime()/s
#14 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setw(10) <<
std::setprecision(3) << digi->GetEnergy()/MeV
#15 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setw(10) <<
std::setprecision(3) << digi->GetGlobalPos().x()/mm
#16 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setw(10) <<
std::setprecision(3) << digi->GetGlobalPos().y()/mm
#17 << " " << std::resetiosflags(std::ios::floatfield) <<
std::setiosflags(std::ios::scientific) << std::setw(10) <<
std::setprecision(3) << digi->GetGlobalPos().z()/mm
#18 << " " << std::setw(4) << digi->GetNPhantomCompton()
#19 << " " << std::setw(4) << digi->GetNCrystalCompton()
#20 << " " << std::setw(4) << digi->GetNPhantomRayleigh()
#21 << " " << std::setw(4) << digi->GetNCrystalRayleigh()
#22 << " " << digi->GetComptonVolumeName()
#23 << " " << digi->GetRayleighVolumeName()
# << G4endl;
There are an additional three columns for Rayleigh scattering
information, column 20, 21 & 23.
Hope this helps?
regards,
Graeme
Graeme O'Keefe
Centre for PET
Austin Hospital
Tel: (613)-9496-5767
Fax: (613) 9458-5023
On 25/05/2005, at 5:49 PM, D.J. van der Laan wrote:
I have a problem with the meaning of the columns in the coincidence
ASCII output. According to the manual and the FAQ the number of columns
in de coincidence file should be 42 for the cylindricalPET system, but
the files Gate produces have 46 columns. When looking at the file I
suspect that there are not 6 volume ID's per event, but 8. Is this
correct?
Thanks,
Jan
--
D.J. van der Laan
Delft University of Technology
Mekelweg 15, room 2.00.020
2629 JB Delft
The Netherlands
E: d.j.vanderlaan at tnw.tudelft.nl
T: +31 15 2783776
F: +31 15 2789011
_______________________________________________
gate-users mailing list
gate-users at lphe1pet1.epfl.ch
http://lphe1pet1.epfl.ch/mailman/listinfo/gate-users
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.opengatecollaboration.org/mailman/private/gate-users/attachments/20050526/3a1d0439/attachment.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: singles_binning.eps
Type: application/octet-stream
Size: 449207 bytes
Desc: singles_binning.eps
URL: <http://lists.opengatecollaboration.org/mailman/private/gate-users/attachments/20050526/3a1d0439/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: sinogram_binning.eps
Type: application/octet-stream
Size: 233940 bytes
Desc: sinogram_binning.eps
URL: <http://lists.opengatecollaboration.org/mailman/private/gate-users/attachments/20050526/3a1d0439/attachment-0001.obj>
More information about the Gate-users
mailing list