diff --git a/.gitattributes b/.gitattributes
index 7d845e399..dc8338a6a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12,6 +12,8 @@
*.v -text -diff
*.s -text -diff
*.scn -text -diff
+*.l -text -diff
+*.root -text -diff
*.gz -text -diff
*.nii -text -diff
*.sh eol=lf
diff --git a/.github/workflows/GHA_increase_disk_space.sh b/.github/workflows/GHA_increase_disk_space.sh
new file mode 100755
index 000000000..292a4cf4a
--- /dev/null
+++ b/.github/workflows/GHA_increase_disk_space.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+df -h
+# locations from the internet, e.g. https://github.com/easimon/maximize-build-space
+# saves about 2GB
+if [ -d /usr/share/dotnet ]; then
+ echo removing dotnet
+ sudo rm -rf /usr/share/dotnet
+fi
+# saves about 10 GB
+if [ -d "$AGENT_TOOLSDIRECTORY" ]; then
+ echo removing agent_tools
+ sudo rm -rf "$AGENT_TOOLSDIRECTORY"
+fi
+# saves about 10 GB
+if [ -d /usr/local/lib/android ]; then
+ echo removing android files
+ sudo rm -rf /usr/local/lib/android
+fi
+if [ -d /opt/ghc ]; then
+ echo removing android files
+ sudo rm -rf /opt/ghc
+fi
+df -h
diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
index 2f5a18405..1be05e812 100644
--- a/.github/workflows/build-test.yml
+++ b/.github/workflows/build-test.yml
@@ -2,7 +2,9 @@ name: Build and ctest and recon_test_pack CI
on:
push:
- branches: [ master ]
+ branches:
+ - master
+ - tof_sino_UCL
paths-ignore:
- '.appveyor.yml'
- 'CITATION.cff'
@@ -12,7 +14,9 @@ on:
- '**/*.tex'
pull_request:
- branches: [ master ]
+ branches:
+ - master
+ - tof_sino_UCL
paths-ignore:
- '.appveyor.yml'
- 'CITATION.cff'
@@ -116,6 +120,18 @@ jobs:
with:
submodules: recursive
+ - name: disk space
+ shell: bash
+ run: |
+ case ${{matrix.os}} in
+ (ubuntu* | macOS*)
+ sudo .github/workflows/GHA_increase_disk_space.sh
+ ;;
+ (windows*)
+ # no idea what to do here
+ ;;
+ esac
+
- name: set_compiler_variables
shell: bash
run: |
@@ -331,14 +347,17 @@ jobs:
if: failure()
with:
name: recon_test_pack_log_files-${{ matrix.os }}-${{ matrix.compiler }}${{ matrix.compiler_version }}-${{ matrix.BUILD_TYPE }}-pp=${{ matrix.parallelproj }}-ROOT=${{ matrix.ROOT }}
- path: ${{ github.workspace }}/recon_test_pack/**/*.log
+ path: |
+ ${{ github.workspace }}/recon_test_pack/**/*.log
+ ${{ github.workspace }}/recon_test_pack/**/my_*v
+ ${{ github.workspace }}/recon_test_pack/**/my_*s
retention-days: 7
# Enable tmate debugging of manually-triggered workflows if the input option was provided
- name: Setup tmate session if triggered
uses: mxschmitt/action-tmate@v3
timeout-minutes: 15
- if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
+ if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled == 'true' }}
- name: examples
shell: bash
diff --git a/.gitignore b/.gitignore
index 5d62b325b..e03751db8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,6 +94,6 @@ install/
examples/ROOT_files/ROOT_STIR_consistency/Gate_macros/main_GATE_macro_test*.mac
examples/ROOT_files/ROOT_STIR_consistency/pretest_output/root_data_test*.root
examples/ROOT_files/ROOT_STIR_consistency/pretest_output/root_header_test*.hroot
-examples/ROOT_files/ROOT_STIR_consistency/pretest_output/root_header_test*_lor_pos.txt
+examples/ROOT_files/ROOT_STIR_consistency/*voxel_data_*.*
examples/ROOT_files/ROOT_STIR_consistency/pretest_output/stir_image*.*
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2873ad6dc..fc7b94e51 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -55,10 +55,10 @@ else()
endif()
####### Set Version number etc
-set(VERSION_MAJOR 5)
-set(VERSION_MINOR 2)
+set(VERSION_MAJOR 6)
+set(VERSION_MINOR 0)
set(VERSION_PATCH 0)
-set(VERSION 050200) # only used in STIRConfig.h.in
+set(VERSION 060000) # only used in STIRConfig.h.in and swig/CMakeLists.txt
set(STIR_VERSION
${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
diff --git a/VERSION.txt b/VERSION.txt
index 91ff57278..419625301 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-5.2.0
+6.0.0-pre
diff --git a/documentation/STIR-glossary.tex b/documentation/STIR-glossary.tex
index d291e3b6d..b4786d0d7 100644
--- a/documentation/STIR-glossary.tex
+++ b/documentation/STIR-glossary.tex
@@ -14,7 +14,7 @@
\end{center}
\begin{center}
-\textit{Version 3.0}\\
+\textit{Version 6.0}\\
Originally based on PARAPET Deliverable 4.1,
Extended for Quantitative Reconstruction and motion compensation
@@ -30,35 +30,47 @@ \section*{Introduction}
of data concepts. However, this is all kept brief. Please read additional material, for instance
[Fah2002].
+Most of specifics of this document related to PET scanners, but a lot of terminology has clear
+correspondence for SPECT. However, most SPECT scanners rotate one or more gamma cameras around
+the patient, as opposed to have a ring of detectors.
+
\section*{Basics}
\begin{description}
-% ROW 2
+
\item[Geometry]
A cylindrical geometry has been chosen to describe positron
tomographs made of a number of adjacent detector rings and reconstructed
image volumes. The geometry supports consequently two principal
directions \textit{axially} along the scanner cylinder and \textit{transaxially}
perpendicular to the cylinder axis.
-% ROW 3
+
\item[Scanner]
Geometrically associated to the cylindrical volume defined by
the inner dimensions of the positron tomograph.
-% ROW 4
+
\item[Detector ring ]
Geometrically associated to the cylindrical volume defined by
the dimensions of a detector ring. Note that because currently
Depth of Interaction is not taken into account, the effective ring radius
used in the building blocks is the sum of the inner ring radius
and an average depth of interaction (e.g. \ensuremath{\sim} 1cm for BGO).
-% ROW 5
+
\item[Detector]
Sometimes called \textbf{detector crystal}. Geometrically associated
to the inner face of a detector element. The \textbf{scanner} is then
considered as a tessellation of \textbf{detectors} constructing adjacent \textbf{rings}.
For many scanners, detectors are organized in a block. For instance,
-on the HR+ scanner, a detector block consists of 8x8 detectors.
-% ROW 6
+on the HR+ scanner, a detector block consists of 8x8 detectors. Current scanners
+have mini-blocks, related to the read-out.
+Blocks are often organised in \textbf{modules}, currently called \textbf{buckets} in STIR.
+
+\item[Time of Flight (TOF) for PET]
+Many modern PET scanners measure the difference in arrival time of the 2 gamma photons with
+a certain \textbf{TOF timing resolution} (often expressed in ps). In current scanners,
+the TOF information is discretised into \textbf{TOF bins}.
+
+
\item[LOR (Line-Of-Response)]
Line joining the centres of two \textbf{detectors}. Ignoring scatter,
attenuation and other physical effects, the average number of
@@ -69,12 +81,13 @@ \section*{Basics}
integral approximation works best for LORs that do not run parallel
to edges within the object. We say that the projector that uses
this model is a \textbf{ray tracing} projector.
-% ROW 7
+
\item[TOR (Tube of response)]
Tube joining two \textbf{detectors}.
-% ROW 8
+
\item[Sinogram]
-Set of \textbf{bins} corresponding to 1 segment and 1 \textbf{axial} position.
+Set of \textbf{bins} corresponding to 1 \textbf{segment} and 1 \textbf{axial} position and (in STIR)
+1 TOF bin.
Before \textbf{axial compression,} this corresponds to LORs in a \textbf{detector} \textbf{ring}
(\textit{direct} \textit{sinogram}) or between two different \textbf{detector} \textbf{rings}
(\textit{oblique} \textit{sinogram}). For a \textbf{scanner} of $n$ \textbf{detector
@@ -83,26 +96,33 @@ \section*{Basics}
compression}, the number of \textbf{direct sinograms} is $2n-1$. Conventionally,
the \textbf{view} angle in an oblique sinogram runs only over 180
degrees, meaning that only half of the detectors in each ring
-are covered. The other half corresponds to the \textbf{sinogram} in
-the opposite \textbf{segment} (with minus the average ring difference),
-% ROW 9
+are covered\footnote{In SPECT, rotations often cover 360 degrees.}.
+The other half corresponds to the \textbf{sinogram} in
+the opposite \textbf{segment} (with minus the average ring difference).
+
+In PET, the number of tangential positions determines the ``fan-size''. Its maximum is
+equal to the number of detectors per ring. Scanners use far less
+you don’t want to look at coincidences between neighbouring crystals!).
+
\item[View]
The azimuthal angle of an \textbf{LOR} (ignoring \textbf{interleaving},
-see the documentation of the ProjDataInfoCylindricalNoArcCorr
-class, and \textbf{mashing}).
-% ROW 10
+see the documentation of the \texttt{ProjDataInfoCylindricalNoArcCorr}
+class).
+The maximum number of views is half the number of detectors per ring
+(this is again due to interleaving).
+
\item[Bin]
A single element in a sinogram, completely specified by its \textbf{segment,
-axial} \textbf{position, view} and \textbf{tangential position}.
-% ROW 11
-\item[Ring difference]
+axial} \textbf{position, view}, \textbf{tangential position} and (in PET) \textbf{TOF bin}.
+
+\item[Ring difference (in PET)]
Number of \textbf{rings} between two \textbf{rings} associated to a \textbf{sinogram}.
If \textit{ringA} and \textit{ringB} are the ring numbers, the \textbf{ring
difference} is given by \textit{ringB} -- \textit{ringA}. Thus there can be \textit{positive}
-and \textit{negative} \textbf{ring differences}. \linebreak
+and \textit{negative} \textbf{ring differences}.\\
The (average) \textbf{ring difference} of a \textbf{direct sinogram} is
zero.
-% ROW 12
+
\item[Michelogram]
Representation of \textbf{sinograms} on a square grid as shown in
Annex 1. If \textit{ringA} and \textit{ringB} are the ring numbers associated
@@ -110,14 +130,14 @@ \section*{Basics}
axis and \textit{ringB} on the vertical axis. \textbf{Positive ring differences}
are below the line representing \textbf{direct sinograms} and \textbf{negative
ring differences} above this line.
-% ROW 13
+
\item[Segment]
Set of \textbf{merged} \textbf{sinograms} with a common average \textbf{ring
difference} as shown in Annex 1.
-% ROW 14
+
\item[Viewgram]
Set of equal azimuth \textbf{merged} \textbf{LORs} of a \textbf{segment}.
-% ROW 15
+
\item[Projection data]
The set of all (measured) LORs, normally split into \textbf{segments} etc.
The word ``projection'' is used because after various corrections and
@@ -129,38 +149,38 @@ \section*{Basics}
cases, the term is also used for the smaller volume for which
there is at least 1 \textbf{bin} with non-zero detection probability
for every \textbf{view}. The latter FOV is usually cylindrical.
-% ROW 16
+
\item[Image slice]
Geometrically associated to a cylindrical volume defined by
a slice of the \textbf{FOV}. By convention, a \textbf{slice} is half the
width of a \textbf{ring}. For a \textbf{scanner} of \textit{n} \textbf{detector} \textbf{rings},
there are 2\textit{n}--1 \textbf{image slices}.
-% ROW 17
+
\item[Direct plane]
\textbf{Image slice} centered on a \textbf{ring}. For a \textbf{scanner} of \textit{n} \textbf{detector} \textbf{rings},
there are \textit{n} \textbf{direct planes}. The \textbf{FOV} is ended by two \textbf{direct
planes} centered on the first and last \textbf{rings}.
-% ROW 18
+
\item[Cross plane]
\textbf{Image slice} in between two consecutive \textbf{direct planes}. \textbf{Direct
planes} are adjacent to \textbf{cross planes}. For a \textbf{scanner} of \textit{n} \textbf{detector} \textbf{rings},
there are 2\textit{n}--1 \textbf{cross planes}.
\end{description}
-\section*{Different data compressions used in PET data}
+\section*{Different (lossy) data compressions used}
\begin{description}
-% ROW 21
+
\item[Trimming]
Reduction of the number of \textbf{bins} in tangential direction without
changing the size of \textbf{bins}. \textbf{Trimming} is a type of \textbf{bin}
truncation.
-% ROW 22
-\item[Angular compression (Mashing)]
+
+\item[Angular compression (view mashing)]
Reduction of the number of \textbf{views} by a multiple of two. As
an example, doing a \textbf{mashing} of 2 means that pairs of \textbf{views}
have been added 2 by 2 to form only one \textbf{view}.
-% ROW 23
-\item[Axial compression (Span)]
+
+\item[Axial compression (Span), PET]
Reduction of the number of \textbf{sinograms} at different \textbf{ring
differences} as shown in Annex 1. \textbf{Span} is a number used by
Siemens/CTI to say how much axial compression has been used.
@@ -173,57 +193,75 @@ \section*{Different data compressions used in PET data}
\textbf{STIR} supports any even span (where segment $0$ has effective
$\mathrm{span}+1$).
Finally, for historical reasons \textbf{STIR} also support a different mixed
-format where segment $0$ has span $3$ but higher segments have span $1$.
+format where segment $0$ has span $3$ but higher segments have span $1$,
+or indeed any mix ``spans-per-segment''.
+
+\item[SSRB]
+Originally, single slice rebinning was developed to collapse 3D PET data into non-oblique sinograms.
+In STIR, it is generalised to combine segments, optionally keeping some oblique segments.
+this effectively increases the \textbf{span}.
+
+\item[TOF mashing (PET)]
+Reduction of the number of \textbf{TOF bins} by combining adjacent bins. The \textbf{TOF mashing factor}
+is defined as the ratio of the \textbf{Maximum number of (unmashed) TOF time bins} supported
+by the scanner (in list-mode) over the actual number of TOF bins. Currently in STIR, this
+ratio has to be an integer. The size of a TOF bin is computed by multiplying the
+\textbf{TOF mashing factor} with the \textbf{size of unmashed TOF time bins}, with the latter
+defined as a scanner property.\\
+Note that many PET scanners use a \textbf{TOF mashing factor} greater than 1
+for their standard histogrammed projection data.
+
\end{description}
\section*{Terms used in quantitative PET reconstruction}
\begin{description}
-% ROW 24
+
\item[Scatter Point]
Coordinate where a scatter event takes place.
-% ROW 25
+
\item[SSS - Single scatter simulation]
Estimation of the probability to measure a coincidence event that one of
the two photons has been scattered only once.
-% ROW 26
+
\item[B-Splines]
Basis splines are a set of polynomial functions that have minimal support with
respect to a given degree, smoothness, and domain partition. In imaging they
are useful for performing very fast multidimensional interpolation calculations.
-% ROW 27
+
+
\item[Inverse-SSRB]
It is the pseudo-inverse operation of single slice rebinning which can be used
as the simplest way to extrapolate direct sinograms into indirect sinograms.
-% ROW 28
+
\item[Plasma Data]
Radioactivity concentration in plasma (and blood) during the scanning acquisition.
Usually it is measured in $\mathit{kBq/cm^3}$ over a time window of 1 second.
-% ROW 29
+
\item[Dynamic Data/Images]
A stack of projection data or images through time.
-% ROW 30
+
\item[Kinetic Model]
The kinetic model describe the tracer exchange between plasma and tissue
and between tissue compartments.
-% ROW 31
+
\item[Kinetic Parameters]
The parameters of the kinetic model which are estimated such that
the model is in agreement with the acquired data.
-% ROW 32
+
\item[(Kinetic) Model Matrix]
Linear kinetic models can be written with compact matrix operations, which
relate the dynamic images and the kinetic parameters with the kinetic model matrix.
This matrix can be seen as the application of the transformation from parametric
domain to the temporal domain.
-% ROW 33
+
\item[Patlak Plot]
For irreversible tracers, after a certain period from tracer injection,
the free tracer in tissue reaches equilibrium with the radiotracer in plasma and
then the original model simplifies to a linear plot known as the Patlak Plot.
-% ROW 34
+
\item[Parametric Image]
An image whose voxels hold the values the kinetic parameters.
-% ROW 35
+
\item[Parametric Image Reconstruction (PIR)]
Estimation of the kinetic parameters from dynamic images for each voxel (indirect PIR).
The parametric images can also be reconstructed directly from dynamic projection data.
@@ -262,8 +300,8 @@ \section*{Terms used in motion-compensated reconstruction}
lines connecting the dots indicate the sinograms that are added
together. The illustration is for \textbf{span} 7=4+3 (this terminology
was introduced because for some axial positions, 4 sinograms
-are added, while for others only 3. Note that \textbf{span} is always
-odd.).
+are added, while for others only 3. Note that for even \textbf{span}, segment 0
+has one more set of axial positions added than the oblique ones.).
\begin{figure}[htbp]
diff --git a/documentation/release_notes_TOF.htm b/documentation/release_notes_TOF.htm
new file mode 100644
index 000000000..6356d8e78
--- /dev/null
+++ b/documentation/release_notes_TOF.htm
@@ -0,0 +1,163 @@
+
+
+
+ Summary of changes in STIR release 6.0
+
+
+
+ Summary of changes in STIR release 6.0
+
+This version is 95% backwards compatible with STIR 5.x for the user (see below).
+Developers might need to make code changes as
+detailed below.
+
+Overall summary
+This release is a major upgrade adding Time of Flight (TOF) capabilities to STIR.
+
+
+
+Of course, there is also the usual code-cleanup and
+improvements to the documentation.
+
+
+This release contains mainly code written by Kris Thielemans (UCL) and Richard Brown (UCL).
+
+
+ Patch release info
+
+ - 5.0.0 released ?/?/2020
+
+
+
+ Summary for end users (also to be read by developers)
+
+Changes breaking backwards compatibility from a user-perspective
+
+ -
+
+
+
+Bug fixes
+
+
+New functionality
+
+ -
+ projectors now have a
clone()
member, currently returning a bare pointer (like other STIR classes)
+
+
+
+
+Changed functionality
+
+ -
+ We now always check (
ProjDataInfo*NoArcCorr
) if number of tangential positions in the projection data exceeds the maximum number
+ of non arc-corrected bins set for the scanner. If it is, an error is raised.
+
+ -
+ Write
STIR6.0
as Interfile key version to denote TOF changes.
+ This is currently ignored for parsing though.
+
+
+
+Changed functionality breaking backwards incompatibility
+
+ -
+
ProjDataInfo::ask_parameters()
and therefore create_projdata_template
+ has changed:
+
+ - If the scanner definition in STIR has TOF capabilities, it will ask for the TOF mashing factor.
+ - The default for arc-correction has changed to N, i.e.
false
.
+ - Default value for span is now 11 for Siemens and 2 for GE scanners.
+ - The span=0 case (i.e. span-3 for segment 0, span=1 for oblique ones, erroneously
+ by STIR used for the GE Advance) is no deprecated. GE uses span=2.
+ (Reading a "span=0" case is still supported")
+
+
+
+
+
+Build system
+
+
+
+Known problems
+
+
+Minor bug fixes
+
+
+Documentation changes
+
+- Added documentation on new features
+- Also check the wiki in addition to the provided PDFs.
+
+
+
+recon_test_pack changes
+
+- updated version number and added some clarification to the README.txt
+
+
+Other changes to tests
+
+
+What's new for developers (aside from what should be obvious
+from the above):
+
+Major bugs fixed
+
+
+Backward incompatibities
+
+ -
+
virtual ListModeData::get_scanner_ptr()
is replaced by ListModeData::get_scanner()
.
+
+ -
+
ProjDataInfo*NoArcCorr::get_bin_for_det_pair
is now private.
+ Use get_bin_for_det_pos_pair
instead.
+
+
+
+New functionality
+
+ Bin
can now be output to stream as text
+ - added
RunTests::check_if_equal
for Bin
+ -
+
KeyParser
has a new facility to add an alias to a keyword. This can be used to rename a keyword
+ for instance while remaining backwards compatible. By default, a warning will be written, but this can be disabled.
+
+
+
+
+Other code changes
+
+
+
+
+
diff --git a/examples/PET_simulation/generate_input_data.sh b/examples/PET_simulation/generate_input_data.sh
index 9d792704e..8aaa7a505 100755
--- a/examples/PET_simulation/generate_input_data.sh
+++ b/examples/PET_simulation/generate_input_data.sh
@@ -42,6 +42,7 @@ cat > my_input.txt < 1:
+ chdir(sys.argv[1])
-# Optional argument to set the directory of the output of the view_offset_root test.
-working_directory = getcwd()
-if len(sys.argv) > 1:
- working_directory = sys.argv[1]
+ nonTOF_evaluation("non_TOF_voxel_data_", file_extension=".csv")
-point_sources_data = dict()
-# Assumeing the prefix's and suffix's of the file names
-filename_prefix = "root_header_test"
-filename_suffix = "_lor_pos.txt"
+ TOF_evaluation("TOF_voxel_data_", file_extension=".csv")
-# Loop over all files in the working directory and load the data into the point_sources_data dictionary
-for i in range(1, 9, 1):
- point_sources_data[i] = ViewOffsetConsistencyClosestLORInfo(f"{filename_prefix}{i}{filename_suffix}")
+ print("Done")
-# Print the number of events, number of failed events and failure percentage for each point source
-print_pass_and_fail()
-# Print the mean offset in each axis (x,y,z) for each point source and the total bias in each axis
-print_axis_biases()
-print("Done")
+if __name__ == "__main__":
+ main()
diff --git a/examples/ROOT_files/ROOT_STIR_consistency/README.md b/examples/ROOT_files/ROOT_STIR_consistency/README.md
index 476fa0f7d..96bdd6e18 100644
--- a/examples/ROOT_files/ROOT_STIR_consistency/README.md
+++ b/examples/ROOT_files/ROOT_STIR_consistency/README.md
@@ -1,61 +1,57 @@
# TOF consistency checks for STIR
+
## Authors: Elise Emond & Robert Twyman
-Copyright (C) 2022, University College London
- This file is part of STIR.
+Copyright (C) 2022, 2024 University College London
+This file is part of STIR.
SPDX-License-Identifier: Apache-2.0
See STIR/LICENSE.txt for details
-These files were included to :
-* Test non-TOF ROOT and STIR consistency, particularly the rotation.
-* Test the TOF STIR implementation was correct (NOT YET IMPLEMENTED).
+These files are included to :
-Directories
------
+- Test non-TOF ROOT and STIR consistency, particularly the rotation.
+- Test the TOF STIR implementation is correct.
-- `SourceFiles/`: Contains 8 `generate_image` parameter files and GATE macro files for the emission source positions. One pair of files for each test.
-- `Gate_macros/`: Contains the GATE macro files for generating the data.
-- `DebugScripts`: Contains scripts for better understanding the tests.
+See src/recon_test/test_consistency_with_GATE.cxx. This test is run automatically when using ctest.
+
+## Directories
+- `SourceFiles/`: Contains 8 `generate_image` parameter files and GATE macro files for the emission source positions. One pair of files for each simulation.
+- `Gate_macros/`: Contains the GATE macro files for generating the data.
+- `DebugScripts/`: Contains scripts for better understanding the tests.
-FILES
-----
+## FILES
- `README.md`: This file.
- `root_header_test_template.hroot`: Template file for generating root header files for each of the test list mode data files that are generated by GATE.
- `run_pretest_script.sh`: The main script for generating the data (requires GATE). This script runs GATE simulations for each test emission and generates the root header files for them.
+---
-______
+## Methodology
-Methodology
-----
- 1. Generate the ROOT data.
- 1. Run `./run_pretest_script.sh` in the terminal to generate the ROOT files (requires Gate) for different point sources, or
- 2. Download the ROOT data and proceed without Gate simulation.
-
- 2. Run the STIR test: `src/recon_test/test_view_offset_root`.
- This test should tell you whether it failed or not by testing if the LOR passes by,
- or close to, the original point source position.
- 3. Run the python scripts in `DebugScripts` to better understand erros and to give a more in-depth analysis.
+1. Get the ROOT data: either
+ - Run `./run_pretest_script.sh` in the terminal to generate the ROOT files (requires Gate) for different point sources, or
+ - Download the ROOT data and proceed without Gate simulation. This is done by ctest when the STIR build was configured with `DOWNLOAD_ZENODO_TEST_DATA=ON`.
+2. Run the STIR test: `src/recon_test/test_consistency_with_GATE` (Best via ctest). This test should tell you whether it failed or not by testing if the LOR passes by, or close to, the original point source position.
+3. Run the python scripts in `DebugScripts` to better understand errors and to give a more in-depth analysis.
+---
-_____
+## SOURCE POSITION CONFIGURATION
-SOURCE POSITION CONFIGURATION
------
In GATE coordinates (`mm`), the point sources positions are as follows:
-| ID | x | y | z | comment |
-| :---: | :---: | :----: | :---: | :---: |
-| **1** | 0 | 0 | 0 | center scanner |
-| **2** | 190 | 0 | 0 | +x |
-| **3** | 0 | 190 | 0 | +y |
-| **4** | 95 | 95 | 0 | +x +y |
-| **5** | 0 | 0 | 70 | +z |
-| **6** | 190 | 0 | 70 | +x +z |
-| **7** | 0 | 190 | 70 | +y +z |
-| **8** | 95 | 95 | 70 | +x +y +z |
+| ID | x | y | z | comment |
+| :---: | :-: | :-: | :-: | :------------: |
+| **1** | 0 | 0 | 0 | center scanner |
+| **2** | 190 | 0 | 0 | +x |
+| **3** | 0 | 190 | 0 | +y |
+| **4** | 95 | 95 | 0 | +x +y |
+| **5** | 0 | 0 | 70 | +z |
+| **6** | 190 | 0 | 70 | +x +z |
+| **7** | 0 | 190 | 70 | +y +z |
+| **8** | 95 | 95 | 70 | +x +y +z |
_Note_: The activity of testIDs 5-8 are 10x that of 1-4 because of the large z-shift.
@@ -66,9 +62,8 @@ This is given by:
```
stir_z = (L-d)/2 + gate_z
-```
+```
where `L` is the scanner z-length (`157.16 mm`) and `d = 6.54mm` is the distance between rings .
For the data currently used in this test, `(L-d)/2 = 75.31mm`.
-There are no translations in x or y.
-
+There are no translations in x or y.
diff --git a/examples/samples/PET_TOF_Interfile_header_Signa_PETMR.hs b/examples/samples/PET_TOF_Interfile_header_Signa_PETMR.hs
new file mode 100644
index 000000000..29cead4dc
--- /dev/null
+++ b/examples/samples/PET_TOF_Interfile_header_Signa_PETMR.hs
@@ -0,0 +1,62 @@
+!INTERFILE :=
+; sample Interfile header, created with create_projdata_template for the GE Signa PET/MR
+; with span=2 and full ring difference (which is how GE compresses their sinograms)
+; (and some edits for clarification)
+; Check PET_Interfile_header.hs for more information and some other keywords
+
+!imaging modality := PT
+name of data file := PET_TOF_Interfile_header_Signa_PETMR.s
+originating system := GE Signa PET/MR
+!version of keys := STIR6.0
+!GENERAL DATA :=
+!GENERAL IMAGE DATA :=
+!type of data := PET
+imagedata byte order := LITTLEENDIAN
+!PET STUDY (General) :=
+!PET data type := Emission
+applied corrections := {None}
+!number format := float
+!number of bytes per pixel := 4
+number of dimensions := 5
+matrix axis label [5] := timing positions
+!matrix size [5] := 27
+matrix axis label [4] := segment
+!matrix size [4] := 45
+matrix axis label [3] := view
+!matrix size [3] := 224
+matrix axis label [2] := axial coordinate
+!matrix size [2] := { 1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61,65,69,73,77,81,85,89,85,81,77,73,69,65,61,57,53,49,45,41,37,33,29,25,21,17,13,9,5,1}
+matrix axis label [1] := tangential coordinate
+!matrix size [1] := 357
+TOF mashing factor := 13
+minimum ring difference per segment := { -44,-43,-41,-39,-37,-35,-33,-31,-29,-27,-25,-23,-21,-19,-17,-15,-13,-11,-9,-7,-5,-3,-1,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44}
+maximum ring difference per segment := { -44,-42,-40,-38,-36,-34,-32,-30,-28,-26,-24,-22,-20,-18,-16,-14,-12,-10,-8,-6,-4,-2,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,44}
+Scanner parameters:=
+ Scanner type := GE Signa PET/MR
+ Number of rings := 45
+ Number of detectors per ring := 448
+ Inner ring diameter (cm) := 62.36
+ Average depth of interaction (cm) := 0.85
+ Distance between rings (cm) := 0.556
+ Default bin size (cm) := 0.201565
+ View offset (degrees) := -5.23
+ Maximum number of non-arc-corrected bins := 357
+ Default number of arc-corrected bins := 331
+ Energy resolution := 0.105
+ Reference energy (in keV) := 511
+ Maximum number of (unmashed) TOF time bins := 351
+ Size of unmashed TOF time bins (ps) := 6.84615
+ TOF timing resolution (ps) := 390
+ Number of blocks per bucket in transaxial direction := 4
+ Number of blocks per bucket in axial direction := 5
+ Number of crystals per block in axial direction := 9
+ Number of crystals per block in transaxial direction := 4
+ Number of detector layers := 1
+ Number of crystals per singles unit in axial direction := 1
+ Number of crystals per singles unit in transaxial direction := 1
+End scanner parameters:=
+
+number of time frames := 1
+start vertical bed position (mm) := 0
+start horizontal bed position (mm) := 0
+!END OF INTERFILE :=
diff --git a/examples/samples/PET_TOF_Interfile_header_Signa_PETMR.s b/examples/samples/PET_TOF_Interfile_header_Signa_PETMR.s
new file mode 100644
index 000000000..e69de29bb
diff --git a/examples/samples/lm_to_projdata.par b/examples/samples/lm_to_projdata.par
index de8690d84..99eaa2191 100644
--- a/examples/samples/lm_to_projdata.par
+++ b/examples/samples/lm_to_projdata.par
@@ -37,5 +37,5 @@ lm_to_projdata Parameters:=
; if you're short of RAM (i.e. a single projdata does not fit into memory),
; you can use this to process the list mode data in multiple passes.
num_segments_in_memory := -1
-
+ num_TOF_bins_in_memory := -1
End :=
diff --git a/recon_test_pack/OSMAPOSL_test_lm.par b/recon_test_pack/OSMAPOSL_test_lm.par
index b53b6ebcc..65a86d430 100755
--- a/recon_test_pack/OSMAPOSL_test_lm.par
+++ b/recon_test_pack/OSMAPOSL_test_lm.par
@@ -1,8 +1,9 @@
OSMAPOSLParameters :=
objective function type:= PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin
PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters:=
- list mode filename := PET_ACQ_small.l.hdr.STIR
- maximum absolute segment number to process := 1
+ list mode filename := ${INPUT}
+ additive sinogram := ${ADD_SINO}
+ maximum absolute segment number to process := ${MAX_SEG_NUM}
Matrix type := Ray Tracing
Ray tracing matrix parameters :=
; use multiple (almost) parallel LORs for every bin in the sinogram
@@ -15,7 +16,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Par
Bin Normalisation type := From ProjData
Bin Normalisation From ProjData :=
- normalisation projdata filename:= my_acfs.hs
+ normalisation projdata filename:= ${NORM_PROJDATA}
End Bin Normalisation From ProjData:=
;num_events_to_use := 100
@@ -25,7 +26,7 @@ PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Par
max cache size := ${cache}
recompute cache := ${recompute_cache}
recompute sensitivity := ${recompute_sensitivity}
- sensitivity filename:= my_sens_t_lm_pr_seg1.hv
+ sensitivity filename:= ${SENS}
zoom := 1
xy output image size (in pixels) := 100
diff --git a/recon_test_pack/OSMAPOSL_test_lmf.par b/recon_test_pack/OSMAPOSL_test_lmf.par
new file mode 100755
index 000000000..775ae5d04
--- /dev/null
+++ b/recon_test_pack/OSMAPOSL_test_lmf.par
@@ -0,0 +1,38 @@
+OSMAPOSLParameters :=
+objective function type:= PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin
+PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters:=
+ list mode filename := PET_ACQ_small.l.hdr.STIR
+ max ring difference num to process := 2
+ projector pair type := Matrix
+ Projector Pair Using Matrix Parameters :=
+ Matrix type := Ray Tracing
+ Ray tracing matrix parameters :=
+ ; use multiple (almost) parallel LORs for every bin in the sinogram
+ ; to avoid discretisation artefacts
+ number of rays in tangential direction to trace for each bin:= 3
+ ; you could disable some symmetries if you have enough memory
+ ; this would for instance allow you to increase the number of subsets
+ ; do_symmetry_90degrees_min_phi:=0
+ End Ray tracing matrix parameters :=
+ End Projector Pair Using Matrix Parameters :=
+
+ Bin Normalisation type := From ProjData
+ Bin Normalisation From ProjData :=
+ normalisation projdata filename:= my_acfs.hs
+ End Bin Normalisation From ProjData:=
+
+ ;num_events_to_store := 100
+ recompute sensitivity :=1
+ use subset sensitivities:= 0
+ sensitivity filename:= my_sens_t_lm_pr_seg2.hv
+ zoom := 1
+
+additive sinogram := my_MLrandoms_f1.hs
+
+end PoissonLogLikelihoodWithLinearModelForMeanAndListModeDataWithProjMatrixByBin Parameters:=
+enforce initial positivity condition:= 1
+number of subsets:= 1
+number of subiterations:= 1
+save estimates at subiteration intervals:= 1
+output filename prefix := my_output_t_lm_pr_seg2
+END :=
diff --git a/recon_test_pack/OSMAPOSL_test_proj.par b/recon_test_pack/OSMAPOSL_test_proj.par
index b16809bd3..c13ed1fb9 100755
--- a/recon_test_pack/OSMAPOSL_test_proj.par
+++ b/recon_test_pack/OSMAPOSL_test_proj.par
@@ -1,8 +1,9 @@
OSMAPOSLParameters :=
objective function type:= PoissonLogLikelihoodWithLinearModelForMeanAndProjData
PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=
- input file := my_sinogram_f1g1d0b0.hs
- maximum absolute segment number to process := 1
+ input file := ${INPUT}
+ additive sinogram := ${ADD_SINO}
+ maximum absolute segment number to process := ${MAX_SEG_NUM}
projector pair type := Matrix
Projector Pair Using Matrix Parameters :=
Matrix type := Ray Tracing
@@ -19,12 +20,12 @@ PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=
Bin Normalisation type := From ProjData
Bin Normalisation From ProjData :=
- normalisation projdata filename:= my_acfs.hs
+ normalisation projdata filename:= ${NORM_PROJDATA}
End Bin Normalisation From ProjData:=
recompute sensitivity := 1
use subset sensitivities:= 0
- sensitivity filename:= my_sens_t_proj_seg1.hv
+ sensitivity filename:= ${SENS}
zoom := 1
xy output image size (in pixels) := 100
diff --git a/recon_test_pack/OSMAPOSL_test_sim_PM.par b/recon_test_pack/OSMAPOSL_test_sim_PM.par
index a8fde10bf..95f252311 100644
--- a/recon_test_pack/OSMAPOSL_test_sim_PM.par
+++ b/recon_test_pack/OSMAPOSL_test_sim_PM.par
@@ -3,7 +3,7 @@ OSMAPOSLParameters :=
objective function type:= PoissonLogLikelihoodWithLinearModelForMeanAndProjData
PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=
-input file := my_prompts.hs
+input file := my_prompts${suffix}.hs
zero end planes of segment 0:= 0 ; segment 0 is has direct and indirect planes
; if disabled, defaults to maximum segment number in the file
;maximum absolute segment number to process := 3
@@ -19,22 +19,22 @@ projector pair type := Matrix
Bin Normalisation type := from projdata
Bin Normalisation From ProjData :=
- normalisation projdata filename:= my_acfs.hs
+ normalisation projdata filename:= my_acfs${suffix}.hs
End Bin Normalisation From ProjData:=
-additive sinogram := my_additive_sinogram.hs
+additive sinogram := my_additive_sinogram${suffix}.hs
xy output image size (in pixels) := 91
zoom := .5
use subset sensitivities:=1
-subset sensitivity filenames:= my_DSTE_sens_2D_PM_s14_%d.hv
+subset sensitivity filenames:= my_test_sim${suffix}_sens_PM_s${num_subsets}_%d.hv
recompute_sensitivity:=1
end PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=
-output filename prefix := my_test_sim_image_OSMAPOSL_PM
-number of subsets:= 14
+output filename prefix := my_test_sim${suffix}_image_OSMAPOSL_PM
+number of subsets:= ${num_subsets}
start at subset:= 0
number of subiterations:= 28
save estimates at subiteration intervals:= 7
diff --git a/recon_test_pack/OSMAPOSL_test_sim_PM_QP.par b/recon_test_pack/OSMAPOSL_test_sim_PM_QP.par
index a42e5a636..d66edf139 100644
--- a/recon_test_pack/OSMAPOSL_test_sim_PM_QP.par
+++ b/recon_test_pack/OSMAPOSL_test_sim_PM_QP.par
@@ -3,7 +3,7 @@ OSMAPOSLParameters :=
objective function type:= PoissonLogLikelihoodWithLinearModelForMeanAndProjData
PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=
-input file := my_prompts.hs
+input file := my_prompts${suffix}.hs
zero end planes of segment 0:= 0 ; segment 0 is has direct and indirect planes
; if disabled, defaults to maximum segment number in the file
;maximum absolute segment number to process := 3
@@ -19,16 +19,16 @@ projector pair type := Matrix
Bin Normalisation type := from projdata
Bin Normalisation From ProjData :=
- normalisation projdata filename:= my_acfs.hs
+ normalisation projdata filename:= my_acfs${suffix}.hs
End Bin Normalisation From ProjData:=
-additive sinogram := my_additive_sinogram.hs
+additive sinogram := my_additive_sinogram${suffix}.hs
xy output image size (in pixels) := 91
zoom := .5
use subset sensitivities:=1
-subset sensitivity filenames:= my_DSTE_sens_2D_PM_s14_%d.hv
+subset sensitivity filenames:= my_test_sim${suffix}_sens_PM_s${num_subsets}_%d.hv
recompute_sensitivity:=1
prior type := quadratic
@@ -39,11 +39,11 @@ prior type := quadratic
end PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=
-output filename prefix := my_test_sim_image_OSMAPOSL_PM_QP
-number of subsets:= 14
+output filename prefix := my_test_sim${suffix}_image_OSMAPOSL_PM_QP
+number of subsets:= ${num_subsets}
start at subset:= 0
number of subiterations:= 28
-save estimates at subiteration intervals:= 14
+save estimates at subiteration intervals:= ${num_subsets}
;report objective function values interval := 1
diff --git a/recon_test_pack/OSSPS_test_sim_PM_QPweights.par b/recon_test_pack/OSSPS_test_sim_PM_QPweights.par
index 2b8bc4737..04f69e1ea 100644
--- a/recon_test_pack/OSSPS_test_sim_PM_QPweights.par
+++ b/recon_test_pack/OSSPS_test_sim_PM_QPweights.par
@@ -3,7 +3,7 @@ OSSPSParameters :=
objective function type:= PoissonLogLikelihoodWithLinearModelForMeanAndProjData
PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=
-input file := my_prompts.hs
+input file := my_prompts${suffix}.hs
zero end planes of segment 0:= 1
; if disabled, defaults to maximum segment number in the file
;maximum absolute segment number to process := 3
@@ -19,13 +19,13 @@ projector pair type := Matrix
Bin Normalisation type := from projdata
Bin Normalisation From ProjData :=
- normalisation projdata filename:= my_acfs.hs
+ normalisation projdata filename:= my_acfs${suffix}.hs
End Bin Normalisation From ProjData:=
-additive sinogram := my_additive_sinogram.hs
+additive sinogram := my_additive_sinogram${suffix}.hs
use subset sensitivities:=1
-subset sensitivity filenames:= my_DSTE_sens_2D_PM_s28_%d.hv
+subset sensitivity filenames:= my_test_sim${suffix}_sens_PM_s${num_subsets}_%d.hv
recompute_sensitivity:=1
prior type := quadratic
@@ -37,9 +37,9 @@ prior type := quadratic
end PoissonLogLikelihoodWithLinearModelForMeanAndProjData Parameters:=
-output filename prefix := my_test_sim_image_OSSPS_PM_QPweights
+output filename prefix := my_test_sim${suffix}_image_OSSPS_PM_QPweights
-number of subsets:= 28
+number of subsets:= ${num_subsets}
start at subset:= 0
number of subiterations:= 56
save estimates at subiteration intervals:= 56
@@ -47,7 +47,7 @@ save estimates at subiteration intervals:= 56
;report objective function values interval := 1
; if next is disabled, defaults to image full of 1s (but that's not good for OSSPS)
; in particular, make sure it has the correct scale
-initial estimate:= my_test_sim_image_OSMAPOSL_PM_14.hv
+initial estimate:= my_test_sim${suffix}_image_OSMAPOSL_PM_14.hv
;enforce initial positivity condition := 1
; here start OSSPS specific values
diff --git a/recon_test_pack/RPTsens_seg3_PM.ahv b/recon_test_pack/RPTsens_seg3_PM.ahv
deleted file mode 100644
index 5e56e67da..000000000
--- a/recon_test_pack/RPTsens_seg3_PM.ahv
+++ /dev/null
@@ -1,18 +0,0 @@
-!INTERFILE :=
-!name of data file := RPTsens_seg3_PM.v
-!total number of images := 31
-!data offset in bytes := 0
-!imagedata byte order := LITTLEENDIAN
-!number format := short float
-!number of bytes per pixel := 4
-matrix axis label [1] := x
-!matrix size [1] := 60
-scaling factor (mm/pixel) [1] := 4.44114
-matrix axis label [2] := y
-!matrix size [2] := 60
-scaling factor (mm/pixel) [2] := 4.44114
-;Correct value is of keyword (commented out)
-;!slice thickness (pixels) := 0.759939
-;Value for Analyze
-!slice thickness (pixels) := 3.375
-!END OF INTERFILE :=
diff --git a/recon_test_pack/RPTsens_seg4.ahv b/recon_test_pack/RPTsens_seg4.ahv
deleted file mode 100644
index 28ff3cb02..000000000
--- a/recon_test_pack/RPTsens_seg4.ahv
+++ /dev/null
@@ -1,15 +0,0 @@
-!INTERFILE :=
-!name of data file := RPTsens_seg4.v
-!total number of images := 31
-!data offset in bytes := 0
-!imagedata byte order := LITTLEENDIAN
-!number format := float
-!number of bytes per pixel := 4
-matrix axis label [1] := x
-!matrix size [1] := 97
-scaling factor (mm/pixel) [1] := 3.1088
-matrix axis label [2] := y
-!matrix size [2] := 97
-scaling factor (mm/pixel) [2] := 3.1088
-!slice thickness (pixels) := 3.375
-!END OF INTERFILE :=
diff --git a/recon_test_pack/Siemens_mMR_seg2.hs b/recon_test_pack/Siemens_mMR_seg2.hs
index 750df2c58..6572e523a 100644
--- a/recon_test_pack/Siemens_mMR_seg2.hs
+++ b/recon_test_pack/Siemens_mMR_seg2.hs
@@ -46,4 +46,6 @@ Number of crystals per singles unit in transaxial direction := 9
end scanner parameters:=
effective central bin size (cm) := 0.208815
number of time frames := 1
+start vertical bed position (mm) := 0
+start horizontal bed position (mm) := 0
!END OF INTERFILE :=
diff --git a/recon_test_pack/correct_projdata_norm_only.par b/recon_test_pack/correct_projdata_norm_only.par
new file mode 100644
index 000000000..eb7635478
--- /dev/null
+++ b/recon_test_pack/correct_projdata_norm_only.par
@@ -0,0 +1,13 @@
+correct_projdata Parameters :=
+
+ input file := ${INPUT}
+ output filename := ${OUTPUT}
+ ; use data (1) or set to one (0) :=
+ apply (1) or undo (0) correction := 1
+
+ Bin Normalisation type := from projdata
+ Bin Normalisation From ProjData :=
+ normalisation projdata filename:= ${MULT}
+ End Bin Normalisation From ProjData:=
+
+END:=
diff --git a/recon_test_pack/lm_generate_atten_cylinder.par b/recon_test_pack/lm_generate_atten_cylinder.par
index 0aa3511bf..c7227c749 100644
--- a/recon_test_pack/lm_generate_atten_cylinder.par
+++ b/recon_test_pack/lm_generate_atten_cylinder.par
@@ -1,8 +1,8 @@
generate_image Parameters :=
output filename:=my_atten_image
-X output image size (in pixels):=111
-Y output image size (in pixels):=111
-Z output image size (in pixels):=65
+X output image size (in pixels):=100
+Y output image size (in pixels):=100
+Z output image size (in pixels):=127
X voxel size (in mm):= 3
Y voxel size (in mm):= 3
Z voxel size (in mm) := 0.40625
@@ -16,7 +16,7 @@ Ellipsoidal Cylinder Parameters:=
radius-x (in mm):=100
radius-y (in mm):=100
length-z (in mm):=110
- origin (in mm):={70,10,20}
+ origin (in mm):={70,0,0}
END:=
value := 0.096
diff --git a/recon_test_pack/lm_to_projdata.par b/recon_test_pack/lm_to_projdata.par
old mode 100755
new mode 100644
index f2361ef1c..f5b1c291c
--- a/recon_test_pack/lm_to_projdata.par
+++ b/recon_test_pack/lm_to_projdata.par
@@ -16,5 +16,6 @@ lm_to_projdata Parameters:=
List event coordinates := 0
; if you're short of RAM (i.e. a single projdata does not fit into memory),
; you can use this to process the list mode data in multiple passes.
- num_segments_in_memory := -1
-End :=
+; num_segments_in_memory := 10
+End :=
+
diff --git a/recon_test_pack/root_header.hroot b/recon_test_pack/root_header.hroot
index fb85f561b..390167ace 100644
--- a/recon_test_pack/root_header.hroot
+++ b/recon_test_pack/root_header.hroot
@@ -8,10 +8,12 @@ Inner ring diameter (cm) := 65.6
Average depth of interaction (cm) := 0.7
Distance between rings (cm) := 0.40625
Default bin size (cm) := 0.208626
-Maximum number of non-arc-corrected bins := 344
-Default number of arc-corrected bins := 344
+Maximum number of non-arc-corrected bins := 501
+Default number of arc-corrected bins := 501
View offset (degrees) := 0
-
+Number of TOF time bins := 5
+Size of timing bin (ps) := 820.00
+Timing resolution (ps) := 400.0
GATE scanner type := GATE_Cylindrical_PET
GATE_Cylindrical_PET Parameters :=
diff --git a/recon_test_pack/run_root_GATE.sh b/recon_test_pack/run_root_GATE.sh
index 070da72ca..9b7edd5c5 100755
--- a/recon_test_pack/run_root_GATE.sh
+++ b/recon_test_pack/run_root_GATE.sh
@@ -11,7 +11,7 @@
#
# Author Nikos Efthimiou, Kris Thielemans
-echo This script should work with STIR version 5.2. If you have
+echo This script should work with STIR version 6.x. If you have
echo a later version, you might have to update your test pack.
echo Please check the web site.
echo
@@ -73,10 +73,7 @@ echo "GATE support detected!"
fi
-echo Executing tests on ROOT files generated by GATE simulations, with cylindrical PET scanners
-
-
-
+echo Executing tests on ROOT files generated by GATE simulations
# first delete any files remaining from a previous run
@@ -87,22 +84,12 @@ export INPUT_ROOT_FILE=test_PET_GATE.root
export INPUT=root_header.hroot
export TEMPLATE=template_for_ROOT_scanner.hs
-#
-# Get the number of events unlisted.
-#
-echo
-echo ------------- Converting ROOT files to ProjData file -------------
-echo
-echo Making ProjData for all events
+echo "------------------------------"
echo Running lm_to_projdata for all events
export OUT_PROJDATA_FILE=my_proj_from_lm_all_events
export EXCLUDE_SCATTERED=0
export EXCLUDE_RANDOM=0
-lm_to_projdata ./lm_to_projdata.par 2>"./my_root_log_all.log"
-all_events=`awk -F ":" '/Number of prompts/ {print $2}' my_root_log_all.log`
-echo "Number of prompts stored in this time period:" ${all_events}
-
log=lm_to_projdata_from_ROOT_all_events.log
lm_to_projdata ./lm_to_projdata.par > ${log} 2>&1
if [ $? -ne 0 ]; then
@@ -114,17 +101,11 @@ else
all_events=$(cat ${log}|grep "Number of prompts stored in this time period" | grep -o -E '[0-9]+')
echo Number of prompts stored in this time period: ${all_events}
- echo
fi
-echo Reading all values from ROOT file
-#
-# Get the number of events directly from the ROOT file
-#
-echo
-echo ------------- Reading values directly from ROOT file -------------
-echo
-cat << EOF > my_root.input
+echo Reading values directly from ROOT file
+log=root_output_all_events.log
+${ROOT} -b -l ${INPUT_ROOT_FILE} << EOF >& ${log}
Coincidences->Draw(">>eventlist","","goff");
Int_t N = eventlist->GetN();
cout<& ${log}
+${ROOT} -b -l ${INPUT_ROOT_FILE} << EOF >& ${log}
Coincidences->Draw(">>eventlist","eventID1 == eventID2 && comptonPhantom1 == 0 && comptonPhantom2 == 0","goff");
Int_t N = eventlist->GetN();
cout<=" 6.0. If you have
echo a later version, you might have to update your test pack.
echo Please check the web site.
echo
@@ -68,150 +68,204 @@ echo "Using `command -v lm_to_projdata`"
ThereWereErrors=0
ErrorLogs=""
-# echo "=== Simulate normalisation data"
-# For attenuation data we are going to use a cylinder in the center,
-# with water attenuation values
-echo "=== Generate attenuation image"
-generate_image lm_generate_atten_cylinder.par
-echo "=== Calculate ACFs"
-calculate_attenuation_coefficients --ACF my_acfs.hs my_atten_image.hv Siemens_mMR_seg2.hs > my_create_acfs.log 2>&1
-if [ $? -ne 0 ]; then
-echo "ERROR running calculate_attenuation_coefficients. Check my_create_acfs.log"; exit 1;
-fi
-
-echo "=== Creating my_test_lm_frame.fdef (time frame definitions)"
-# Note: test data contains only 612 ms of data, so use a very short frame of 0.5s
-rm -f my_test_lm_frame.fdef
-echo "0 0.1" > my_test_lm_frame.fdef # skip the first .1s, to test if this feature works
-echo "1 0.5" >> my_test_lm_frame.fdef
-export FRAMES
-
-for use_frame in true false; do
- if $use_frame; then
- FRAMES=my_test_lm_frame.fdef
- suffix=frame
- else
- FRAMES=""
- suffix=counts
- fi
- echo "=============== Using ${suffix} definition ============="
-
- echo "=== Reconstruct listmode data without cache"
- export filename=my_output_t_lm_pr_seg1_${suffix}
- export cache=0
- export recompute_cache=0
- export recompute_sensitivity=1
- logfile=OSMAPOSL_test_lm_${suffix}_1.log
- if ${MPIRUN} OSMAPOSL OSMAPOSL_test_lm.par > "$logfile" 2>&1
- then
- echo "---- Executable ran ok"
+for TOForNOT in "nonTOF" "TOF"; do
+ if [ "$TOForNOT" = "nonTOF" ]; then
+ # non-TOF data
+ export INPUT=PET_ACQ_small.l.hdr.STIR
+ export TEMPLATE=Siemens_mMR_seg2.hs
+ # Note: test data contains only 612 ms of data, so use a very short frame of 0.5s
+ FRAME_DURATION=0.5
+ export NORM_PROJDATA=my_acfs_nonTOF_lmtest.hs # only ACFs in this test
else
- echo "---- There were problems here!"
- ThereWereErrors=1;
- ErrorLogs="$ErrorLogs $logfile"
+ # TOF data
+ log=lm_to_projdata_input_formats.log
+ lm_to_projdata --input-formats > ${log} 2>&1
+ if ! grep -q ROOT lm_to_projdata_input_formats.log; then
+ echo "ROOT support has not been installed in this STIR version."
+ echo "Unfortunately, we cannot do the listmode TOF tests at the moment"
+ echo "as we don't have a TOF listmode sample file in the distribution."
+ continue
+ fi
+ export INPUT_ROOT_FILE=test_PET_GATE.root
+ export INPUT=root_header.hroot
+ export TEMPLATE=template_for_ROOT_scanner.hs
+ FRAME_DURATION=30
+ export NORM_PROJDATA=my_acfs_TOF_lmtest.hs # only ACFs in this test
fi
- echo "=== Reconstruct listmode data with cache and store it on disk"
- # first remove all cached files
- rm -f my_CACHE*bin
- export filename=my_output_t_lm_pr_seg1_${suffix}_with_new_cache
- export cache=5000
- export recompute_cache=1
- export recompute_sensitivity=0
- logfile=OSMAPOSL_test_lm_${suffix}_2.log
- if ${MPIRUN} OSMAPOSL OSMAPOSL_test_lm.par > "$logfile" 2>&1
- then
- echo "---- Executable ran ok"
- else
- echo "---- There were problems here!"
- ThereWereErrors=1;
- ErrorLogs="$ErrorLogs $logfile"
- fi
+ export MAX_SEG_NUM=1
+ echo ""
+ echo "============= $TOForNOT tests using $INPUT =========="
+ echo ""
- echo "=== Compare reconstructed images with and without caching LM file"
- logfile=my_output_comparison_nocache_vs_new_cache_${suffix}.log
- if compare_image my_output_t_lm_pr_seg1_${suffix}_1.hv my_output_t_lm_pr_seg1_${suffix}_with_new_cache_1.hv > "$logfile" 2>&1
- then
- echo "---- This test seems to be ok !"
- else
- echo "---- There were problems here!"
- ThereWereErrors=1;
- ErrorLogs="$ErrorLogs $logfile"
+ # echo "=== Simulate normalisation data"
+ # For attenuation data we are going to use a cylinder in the center,
+ # with water attenuation values
+ echo "=== Generate attenuation image"
+ generate_image lm_generate_atten_cylinder.par
+ echo "=== Calculate ACFs"
+ log="create_${NORM_PROJDATA}.log"
+ calculate_attenuation_coefficients --ACF ${NORM_PROJDATA} my_atten_image.hv ${TEMPLATE} > "$log" 2>&1
+ if [ $? -ne 0 ]; then
+ echo "ERROR running calculate_attenuation_coefficients. Check ${log}"; exit 1;
fi
- echo "=== Reconstruct listmode data with cache loaded from the disk"
- export filename=my_output_t_lm_pr_seg1_${suffix}_with_old_cache
- export cache=40000
- export recompute_cache=0
- export recompute_sensitivity=0
- logfile=OSMAPOSL_test_lm_${suffix}_3.log
- if ${MPIRUN} OSMAPOSL OSMAPOSL_test_lm.par > "$logfile" 2>&1
- then
- echo "---- Executable ran ok"
- else
- echo "---- There were problems here!"
- ThereWereErrors=1;
- ErrorLogs="$ErrorLogs $logfile"
- fi
+ echo "=== Creating my_test_lm_frame.fdef (time frame definitions)"
+ rm -f my_test_lm_frame.fdef
+ echo "0 0.1" > my_test_lm_frame.fdef # skip the first .1s, to test if this feature works
+ echo "1 $FRAME_DURATION" >> my_test_lm_frame.fdef
+ export FRAMES
- echo "=== Compare reconstructed images without caching LM file and with loading cache from disk"
- logfile=my_output_comparison_nocache_vs_existing_cache_${suffix}.log
- if compare_image my_output_t_lm_pr_seg1_${suffix}_1.hv my_output_t_lm_pr_seg1_${suffix}_with_old_cache_1.hv > "$logfile" 2>&1
- then
- echo "---- This test seems to be ok !"
- else
- echo "---- There were problems here!"
- ThereWereErrors=1;
- ErrorLogs="$ErrorLogs $logfile"
- fi
+ for use_frame in true false; do
+ if $use_frame; then
+ FRAMES=my_test_lm_frame.fdef
+ suffix="frame_$TOForNOT"
+ else
+ FRAMES=""
+ suffix="counts_$TOForNOT"
+ fi
+ echo "=============== Using ${suffix} definition ============="
- # create sinograms
- echo "=== Unlist listmode data (for comparison)"
- logfile=lm_to_projdata_${suffix}.log
- if env INPUT=PET_ACQ_small.l.hdr.STIR TEMPLATE=Siemens_mMR_seg2.hs OUT_PROJDATA_FILE=my_sinogram lm_to_projdata lm_to_projdata.par > "$logfile" 2>&1
- then
- echo "---- Executable ran ok"
- else
- echo "---- There were problems here!"
- ThereWereErrors=1;
- ErrorLogs="$ErrorLogs $logfile"
- fi
+ # create sinograms
+ echo "=== Unlist listmode data (for comparison)"
+ logfile=lm_to_projdata_${suffix}.log
+ export OUT_PROJDATA_FILE="my_sinogram_${suffix}"
+ if lm_to_projdata lm_to_projdata.par > "$logfile" 2>&1
+ then
+ echo "---- Executable ran ok"
+ else
+ echo "---- There were problems here! Check $logfile"
+ exit 1 # abort as the rest will fail anyway
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ fi
- echo "=== Reconstruct projection data for comparison"
- export filename=my_output_t_proj_seg1_${suffix}
- export recompute_sensitivity=1
- logfile=OSMAPOSL_test_proj_${suffix}.log
- if ${MPIRUN} OSMAPOSL OSMAPOSL_test_proj.par > "$logfile" 2>&1
- then
- echo "---- Executable ran ok"
- else
- echo "---- There were problems here!"
- ThereWereErrors=1;
- ErrorLogs="$ErrorLogs $logfile"
- fi
+ export ADD_SINO="my_additive_sinogram_${suffix}.hs"
+ echo "=== Create additive sino ${ADD_SINO}"
+ # Just create a constant sinogram with a value max_prompts/50
+ add_value=`list_projdata_info --max "${OUT_PROJDATA_FILE}_f1g1d0b0.hs" 2> /dev/null |grep max|awk -F: '{print $2/50}'`
+ logfile=stir_math_add_sino_${suffix}.log
+ if stir_math -s --including-first --times-scalar 0 --add-scalar "$add_value" "$ADD_SINO" "${OUT_PROJDATA_FILE}_f1g1d0b0.hs" > "$logfile" 2>&1
+ then
+ echo "---- Executable ran ok"
+ else
+ echo "---- There were problems here! Check $logfile"
+ exit 1 # abort as the rest will fail anyway
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ fi
- echo "=== Compare sensitivity images"
- logfile=my_sens_comparison_${suffix}.log
- if compare_image my_sens_t_proj_seg1.hv my_sens_t_lm_pr_seg1.hv > "$logfile" 2>&1
- then
- echo "---- This test seems to be ok !"
- else
- echo "---- There were problems here!"
- ThereWereErrors=1;
- ErrorLogs="$ErrorLogs $logfile"
- fi
+ echo "=== Reconstruct listmode data without cache"
+ export filename=my_output_t_lm_pr_seg${MAX_SEG_NUM}_${suffix}
+ SENS_lm=my_sens_t_lm_pr_seg${MAX_SEG_NUM}_${suffix}.hv
+ export cache=0
+ export recompute_cache=0
+ export recompute_sensitivity=1
+ logfile=OSMAPOSL_test_lm_${suffix}_1.log
+ if env SENS=${SENS_lm} ${MPIRUN} OSMAPOSL OSMAPOSL_test_lm.par > "$logfile" 2>&1
+ then
+ echo "---- Executable ran ok"
+ else
+ echo "---- There were problems here!"
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ break
+ fi
- echo "=== Compare reconstructed images"
- logfile=my_output_comparison_proj_vs_lm_${suffix}.log
- if compare_image my_output_t_proj_seg1_${suffix}_1.hv my_output_t_lm_pr_seg1_${suffix}_1.hv > "$logfile" 2>&1
- then
- echo "---- This test seems to be ok !"
- else
- echo "---- There were problems here!"
- ThereWereErrors=1;
- ErrorLogs="$ErrorLogs $logfile"
- fi
-done
+ echo "=== Reconstruct listmode data with cache and store it on disk"
+ # first remove all cached files
+ rm -f my_CACHE*bin
+ export filename=my_output_t_lm_pr_seg${MAX_SEG_NUM}_${suffix}_with_new_cache
+ export cache=5000
+ export recompute_cache=1
+ export recompute_sensitivity=0
+ logfile=OSMAPOSL_test_lm_${suffix}_2.log
+ if env SENS=${SENS_lm} ${MPIRUN} OSMAPOSL OSMAPOSL_test_lm.par > "$logfile" 2>&1
+ then
+ echo "---- Executable ran ok"
+ else
+ echo "---- There were problems here!"
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ break
+ fi
+
+ echo "=== Compare reconstructed images with and without caching LM file"
+ logfile=my_output_comparison_nocache_vs_new_cache_${suffix}.log
+ if compare_image my_output_t_lm_pr_seg${MAX_SEG_NUM}_${suffix}_1.hv my_output_t_lm_pr_seg${MAX_SEG_NUM}_${suffix}_with_new_cache_1.hv > "$logfile" 2>&1
+ then
+ echo "---- This test seems to be ok !"
+ else
+ echo "---- There were problems here!"
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ fi
+
+ echo "=== Reconstruct listmode data with cache loaded from the disk"
+ export filename=my_output_t_lm_pr_seg${MAX_SEG_NUM}_${suffix}_with_old_cache
+ export cache=40000
+ export recompute_cache=0
+ export recompute_sensitivity=0
+ logfile=OSMAPOSL_test_lm_${suffix}_3.log
+ if env SENS=${SENS_lm} ${MPIRUN} OSMAPOSL OSMAPOSL_test_lm.par > "$logfile" 2>&1
+ then
+ echo "---- Executable ran ok"
+ else
+ echo "---- There were problems here!"
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ break
+ fi
+
+ echo "=== Compare reconstructed images without caching LM file and with loading cache from disk"
+ logfile=my_output_comparison_nocache_vs_existing_cache_${suffix}.log
+ if compare_image my_output_t_lm_pr_seg${MAX_SEG_NUM}_${suffix}_1.hv my_output_t_lm_pr_seg${MAX_SEG_NUM}_${suffix}_with_old_cache_1.hv > "$logfile" 2>&1
+ then
+ echo "---- This test seems to be ok !"
+ else
+ echo "---- There were problems here!"
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ fi
+
+ echo "=== Reconstruct projection data for comparison"
+ SENS_proj=my_sens_t_proj_seg${MAX_SEG_NUM}_${suffix}.hv
+ export filename=my_output_t_proj_seg${MAX_SEG_NUM}_${suffix}
+ export recompute_sensitivity=1
+ logfile=OSMAPOSL_test_proj_${suffix}.log
+ if env SENS="${SENS_proj}" INPUT="${OUT_PROJDATA_FILE}_f1g1d0b0.hs" ${MPIRUN} OSMAPOSL OSMAPOSL_test_proj.par > "$logfile" 2>&1
+ then
+ echo "---- Executable ran ok"
+ else
+ echo "---- There were problems here!"
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ fi
+
+ echo "=== Compare sensitivity images"
+ logfile=my_sens_comparison_${suffix}.log
+ if compare_image ${SENS_proj} ${SENS_lm} > "$logfile" 2>&1
+ then
+ echo "---- This test seems to be ok !"
+ else
+ echo "---- There were problems here!"
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ fi
+
+ echo "=== Compare reconstructed images"
+ logfile=my_output_comparison_proj_vs_lm_${suffix}.log
+ if compare_image my_output_t_proj_seg${MAX_SEG_NUM}_${suffix}_1.hv my_output_t_lm_pr_seg${MAX_SEG_NUM}_${suffix}_1.hv > "$logfile" 2>&1
+ then
+ echo "---- This test seems to be ok !"
+ else
+ echo "---- There were problems here!"
+ ThereWereErrors=1;
+ ErrorLogs="$ErrorLogs $logfile"
+ fi
+ done # frame or counts
+
+done # TOForNOT
echo
echo '--------------- End of tests -------------'
diff --git a/recon_test_pack/run_test_simulate_and_recon.sh b/recon_test_pack/run_test_simulate_and_recon.sh
index b277fa3fe..21dae5415 100755
--- a/recon_test_pack/run_test_simulate_and_recon.sh
+++ b/recon_test_pack/run_test_simulate_and_recon.sh
@@ -3,7 +3,7 @@
#
# Copyright (C) 2011 - 2011-01-14, Hammersmith Imanet Ltd
# Copyright (C) 2011-07-01 - 2011, Kris Thielemans
-# Copyright (C) 2014, University College London
+# Copyright (C) 2014, 2022 University College London
# This file is part of STIR.
#
# SPDX-License-Identifier: Apache-2.0
@@ -19,7 +19,7 @@ if [ -n "$TRAVIS" -o -n "$GITHUB_WORKSPACE" ]; then
set -e
fi
-echo This script should work with STIR version 5.2. If you have
+echo This script should work with STIR version 6.0. If you have
echo a later version, you might have to update your test pack.
echo Please check the web site.
echo
@@ -28,7 +28,6 @@ echo
# Options
#
MPIRUN=""
-
#
# Parse option arguments (--)
# Note that the -- is required to suppress interpretation of $1 as options
@@ -70,7 +69,6 @@ LC_ALL=C
export LC_ALL
./simulate_PET_data_for_tests.sh
-
if [ $? -ne 0 ]; then
echo "Error running simulation"
exit 1
@@ -82,6 +80,13 @@ if [ $? -ne 0 ]; then
echo "Error running simulation with zero view offset"
exit 1
fi
+## TOF data
+TOF_suffix=_TOF
+./simulate_PET_data_for_tests.sh --TOF --suffix "$TOF_suffix"
+if [ $? -ne 0 ]; then
+ echo "Error running simulation"
+ exit 1
+fi
error_log_files=""
@@ -96,70 +101,86 @@ input_ROI_mean=`awk 'NR>2 {print $2}' ${input_image}.roistats`
# the OSSPS par file uses an OSMAPOSL result as initial image
# and reuses its subset sensitivities
for recon in FBP2D FBP3DRP OSMAPOSL OSSPS; do
- echo "Using `command -v ${recon}`"
+ echo "========== Testing `command -v ${recon}`"
for parfile in ${recon}_test_sim*.par; do
- echo "============================================="
- # test first if analytic reconstruction and if so, run pre-correction
- isFBP=0
- if expr ${recon} : FBP > /dev/null; then
- isFBP=1
- suffix=$zero_view_suffix
- export suffix
- echo "Running precorrection"
- correct_projdata correct_projdata_simulation.par > my_correct_projdata_simulation.log 2>&1
+ for dataSuffix in "" "$TOF_suffix"; do
+ echo "===== data suffix: \"$dataSuffix\""
+ # test first if analytic reconstruction and if so, run pre-correction
+ isFBP=0
+ if expr "$recon" : FBP > /dev/null; then
+ if expr "$dataSuffix" : '.*TOF.*' > /dev/null; then
+ echo "Skipping TOF as not yet supported for FBP"
+ break
+ fi
+ isFBP=1
+ suffix=$zero_view_suffix
+ export suffix
+ echo "Running precorrection"
+ correct_projdata correct_projdata_simulation.par > my_correct_projdata_simulation.log 2>&1
+ if [ $? -ne 0 ]; then
+ echo "Error running precorrection. CHECK my_correct_projdata_simulation.log"
+ error_log_files="${error_log_files} my_correct_projdata_simulation.log"
+ break
+ fi
+ else
+ suffix="$dataSuffix"
+ export suffix
+ # we simulate 2 different scanners for non-TOF and TOF that sadly need different number of subsets
+ if expr "$dataSuffix" : '.*TOF.*' > /dev/null; then
+ num_subsets=12
+ else
+ num_subsets=14
+ fi
+ export num_subsets
+ fi
+
+ # run actual reconstruction
+ echo "Running ${recon} ${parfile}"
+ logfile="my_${parfile}${suffix}.log"
+ ${MPIRUN} ${recon} ${parfile} > "$logfile" 2>&1
if [ $? -ne 0 ]; then
- echo "Error running precorrection. CHECK my_correct_projdata_simulation.log"
- error_log_files="${error_log_files} my_correct_projdata_simulation.log"
- break
+ echo "Error running reconstruction. CHECK RECONSTRUCTION LOG \"$logfile\""
+ error_log_files="${error_log_files} "$logfile""
+ break
fi
- else
- suffix=""
- export suffix
- fi
-
- # run actual reconstruction
- echo "Running ${recon} ${parfile}"
- ${MPIRUN} ${recon} ${parfile} > my_${parfile}.log 2>&1
- if [ $? -ne 0 ]; then
- echo "Error running reconstruction. CHECK RECONSTRUCTION LOG my_${parfile}.log"
- error_log_files="${error_log_files} my_${parfile}.log"
- break
- fi
-
- # find filename of (last) image from ${parfile}
- output_filename=`awk -F':=' '/output[ _]*filename[ _]*prefix/ { value=$2;gsub(/[ \t]/, "", value); printf("%s", value) }' ${parfile}`
- if [ ${isFBP} -eq 0 ]; then
- # iterative algorithm, so we need to append the num_subiterations
- num_subiterations=`awk -F':=' '/number[ _]*of[ _]*subiterations/ { value=$2;gsub(/[ \t]/, "", value); printf("%s", value) }' ${parfile}`
- output_filename=${output_filename}_${num_subiterations}
- fi
- output_image=${output_filename}.hv
-
- # compute ROI value
- list_ROI_values ${output_image}.roistats ${output_image} ${ROI} 0 > ${output_image}.roistats.log 2>&1
- if [ $? -ne 0 ]; then
- echo "Error running list_ROI_values. CHECK LOG ${output_image}.roistats.log"
- error_log_files="${error_log_files} ${output_image}.roistats.log"
- break
- fi
-
- # compare ROI value
- output_voxel_size_x=`stir_print_voxel_sizes.sh ${output_image}|awk '{print $3}'`
- output_ROI_mean=`awk "NR>2 {print \\$2*${input_voxel_size_x}/${output_voxel_size_x}}" ${output_image}.roistats`
- echo "Input ROI mean: $input_ROI_mean"
- echo "Output ROI mean: $output_ROI_mean"
- error_bigger_than_1percent=`echo $input_ROI_mean $output_ROI_mean| awk '{ print(($2/$1 - 1)*($2/$1 - 1)>0.0001) }'`
- if [ ${error_bigger_than_1percent} -eq 1 ]; then
- echo "DIFFERENCE IN ROI VALUES IS TOO LARGE. CHECK RECONSTRUCTION LOG my_${parfile}.log"
- error_log_files="${error_log_files} my_${parfile}.log"
- else
- echo "This seems fine."
- fi
-
- echo "============================================="
+
+ # find filename of (last) image from ${parfile}
+ output_filename=`awk -F':=' '/output[ _]*filename[ _]*prefix/ { value=$2;gsub(/[ \t]/, "", value); printf("%s", value) }' "$parfile"`
+ # substitute env variables (e.g. to fill in suffix)
+ output_filename=`eval echo "${output_filename}"`
+ if [ ${isFBP} -eq 0 ]; then
+ # iterative algorithm, so we need to append the num_subiterations
+ num_subiterations=`awk -F':=' '/number[ _]*of[ _]*subiterations/ { value=$2;gsub(/[ \t]/, "", value); printf("%s", value) }' ${parfile}`
+ output_filename=${output_filename}_${num_subiterations}
+ fi
+ output_image=${output_filename}.hv
+
+ # compute ROI value
+ list_ROI_values ${output_image}.roistats ${output_image} ${ROI} 0 > ${output_image}.roistats.log 2>&1
+ if [ $? -ne 0 ]; then
+ echo "Error running list_ROI_values. CHECK LOG ${output_image}.roistats.log"
+ error_log_files="${error_log_files} ${output_image}.roistats.log"
+ break
+ fi
+
+ # compare ROI value
+ output_voxel_size_x=`stir_print_voxel_sizes.sh ${output_image}|awk '{print $3}'`
+ output_ROI_mean=`awk "NR>2 {print \\$2*${input_voxel_size_x}/${output_voxel_size_x}}" ${output_image}.roistats`
+ echo "Input ROI mean: $input_ROI_mean"
+ echo "Output ROI mean: $output_ROI_mean"
+ error_bigger_than_1percent=`echo $input_ROI_mean $output_ROI_mean| awk '{ print(($2/$1 - 1)*($2/$1 - 1)>0.0001) }'`
+ if [ ${error_bigger_than_1percent} -eq 1 ]; then
+ echo "DIFFERENCE IN ROI VALUES IS TOO LARGE. CHECK RECONSTRUCTION LOG "$logfile""
+ error_log_files="${error_log_files} ${logfile}"
+ else
+ echo "This seems fine."
+ fi
+
+ done
done
done
+echo "============================================="
if [ -z "${error_log_files}" ]; then
echo "All tests OK!"
echo "You can remove all output using \"rm -f my_*\""
diff --git a/recon_test_pack/run_test_simulate_and_recon_with_motion.sh b/recon_test_pack/run_test_simulate_and_recon_with_motion.sh
index 46527cc98..2dec1319a 100755
--- a/recon_test_pack/run_test_simulate_and_recon_with_motion.sh
+++ b/recon_test_pack/run_test_simulate_and_recon_with_motion.sh
@@ -113,16 +113,16 @@ if [ $? -ne 0 ]; then
echo "ERROR running warp_and_accumulate_gated_images"; exit 1;
fi
-echo "=== create template sinogram (DSTE in 3D with max ring diff 2 to save time)"
-template_sino=my_DSTE_3D_rd2_template.hs
+echo "=== create template sinogram (DSTE in 3D with max ring diff 3 to save time)"
+template_sino=my_DSTE_3D_rd3_template.hs
cat > my_input.txt < my_create_${template_sino}.log 2>&1
if [ $? -ne 0 ]; then
diff --git a/recon_test_pack/run_test_zoom_image.sh b/recon_test_pack/run_test_zoom_image.sh
index d53a2280a..9d1d42cd6 100755
--- a/recon_test_pack/run_test_zoom_image.sh
+++ b/recon_test_pack/run_test_zoom_image.sh
@@ -76,7 +76,7 @@ get_ROI_value() {
# warning: return 0 if they are different (which is non-standard I guess)
compare_values() {
if [ $# -ne 3 ]; then
- echo "Something wrong with call to compare_values"
+ echo "Someting wrong with call to compare_values"
exit 1
fi
error_bigger_than_x=`echo $1 $2 $3 | awk '{ print(($2/$1 - 1)*($2/$1 - 1)> ($3 * $3)) }'`
@@ -143,16 +143,16 @@ fi
echo "=== make attenuation image"
generate_image generate_atten_cylinder.par
-echo "=== create template sinogram (DSTE in 3D with max ring diff 2 to save time)"
-template_sino=my_DSTE_3D_rd2_template.hs
+echo "=== create template sinogram (DSTE in 3D with max ring diff 3 to save time)"
+template_sino=my_DSTE_3D_rd3_template.hs
cat > my_input.txt < my_create_${template_sino}.log 2>&1
if [ $? -ne 0 ]; then
@@ -180,7 +180,7 @@ new_sum=`list_projdata_info --sum my_prompts.hs | awk -F: '/sum/{ print $2}'`
if compare_values $org_sum $new_sum .01
then
- echo "DIFFERENCE IN su of prompts IS TOO LARGE."
+ echo "DIFFERENCE IN sum of prompts IS TOO LARGE."
exit 1
fi
diff --git a/recon_test_pack/scatter_cylinder.hs b/recon_test_pack/scatter_cylinder.hs
index 6f8ebc079..25d78514a 100644
--- a/recon_test_pack/scatter_cylinder.hs
+++ b/recon_test_pack/scatter_cylinder.hs
@@ -34,7 +34,7 @@ Average depth of interaction (cm) := 0.84
Distance between rings (cm) := 1.962
Default bin size (cm) := -1
View offset (degrees) := 0
-Maximum number of non-arc-corrected bins := 0
+Maximum number of non-arc-corrected bins := 35
Default number of arc-corrected bins := 0
Number of blocks per bucket in transaxial direction := 1
Number of blocks per bucket in axial direction := 1
diff --git a/recon_test_pack/simulate_PET_data_for_tests.sh b/recon_test_pack/simulate_PET_data_for_tests.sh
index 3eece2ba8..e261877eb 100755
--- a/recon_test_pack/simulate_PET_data_for_tests.sh
+++ b/recon_test_pack/simulate_PET_data_for_tests.sh
@@ -7,7 +7,7 @@
#
# Copyright (C) 2011 - 2011-01-14, Hammersmith Imanet Ltd
# Copyright (C) 2011-07-01 - 2011, Kris Thielemans
-# Copyright (C) 2014,2020 University College London
+# Copyright (C) 2014, 2020, 2022 University College London
# This file is part of STIR.
#
# SPDX-License-Identifier: Apache-2.0
@@ -17,7 +17,7 @@
# Author Kris Thielemans
#
-echo This script should work with STIR version 5.2. If you have
+echo This script should work with STIR version 6.x. If you have
echo a later version, you might have to update your test pack.
echo Please check the web site.
echo
@@ -26,6 +26,7 @@ command -v generate_image >/dev/null 2>&1 || { echo "generate_image not found or
echo "Using `command -v generate_image`"
force_zero_view_offset=0
+TOF=0
suffix=""
#
# Parse option arguments (--)
@@ -38,6 +39,9 @@ do
if test "$1" = "--force_zero_view_offset"
then
force_zero_view_offset=1
+ elif test "$1" = "--TOF"
+ then
+ TOF=1
elif test "$1" = "--suffix"
then
suffix="$2"
@@ -72,9 +76,10 @@ echo "=== make emission image"
generate_image generate_uniform_cylinder.par
echo "=== make attenuation image"
generate_image generate_atten_cylinder.par
-echo "=== create template sinogram (DSTE in 3D with max ring diff 2 to save time)"
-template_sino=my_DSTE_3D_rd2_template.hs
-cat > my_input.txt < my_input.txt < my_input.txt < my_create_${template_sino}.log 2>&1
if [ $? -ne 0 ]; then
echo "ERROR running create_projdata_template. Check my_create_${template_sino}.log"; exit 1;
@@ -95,6 +115,10 @@ awk '/END OF INTERFILE/ { print "number of energy windows := 1\nenergy window lo
mv tmp_header.hs ${template_sino}
if [ $force_zero_view_offset -eq 1 ]; then
+ if [ "$TOF" -eq 1 ]; then
+ echo "$0 would need work to be used with both TOF and zero-offset. Exiting"
+ exit 1
+ fi
new_template_sino=my_DSTE_3D_rd2_template$suffix.hs
force_view_offset_to_zero.sh ${new_template_sino} ${template_sino}
template_sino=${new_template_sino}
diff --git a/recon_test_pack/simulate_data.sh b/recon_test_pack/simulate_data.sh
index 55156cdcb..d8ed162eb 100755
--- a/recon_test_pack/simulate_data.sh
+++ b/recon_test_pack/simulate_data.sh
@@ -30,15 +30,15 @@ if [ $# -gt 4 ]; then
suffix=$5
fi
echo "=== create ACFs"
-calculate_attenuation_coefficients --ACF my_acfs$suffix.hs ${atten_image} ${template_sino} forward_projector_proj_matrix_ray_tracing.par > my_create_acfs.log 2>&1
+calculate_attenuation_coefficients --ACF my_acfs$suffix.hs ${atten_image} ${template_sino} forward_projector_proj_matrix_ray_tracing.par > my_create_acfs${suffix}.log 2>&1
if [ $? -ne 0 ]; then
- echo "ERROR running calculate_attenuation_coefficients. Check my_create_acfs.log"; exit 1;
+ echo "ERROR running calculate_attenuation_coefficients. Check my_create_acfs${suffix}.log"; exit 1;
fi
echo "=== create line integrals"
-forward_project my_line_integrals$suffix.hs ${emission_image} ${template_sino} forward_projector_proj_matrix_ray_tracing.par > my_create_line_integrals.log 2>&1
+forward_project my_line_integrals$suffix.hs ${emission_image} ${template_sino} forward_projector_proj_matrix_ray_tracing.par > my_create_line_integrals${suffix}.log 2>&1
if [ $? -ne 0 ]; then
- echo "ERROR running forward_project. Check my_create_line_integrals.log"; exit 1;
+ echo "ERROR running forward_project. Check my_create_line_integrals${suffix}.log"; exit 1;
fi
@@ -53,22 +53,27 @@ fi
echo "=== create norm factors"
# currently just 1 as not used in rest of script yet.
stir_math -s --including-first \
- --times-scalar 0 --add-scalar 1 my_norm$suffix.hs my_line_integrals$suffix.hs
+ --times-scalar 0 --add-scalar 1 my_norm$suffix.hs my_acfs$suffix.hs
echo "=== create prompts"
-export suffix # used in the .par file to determine filenames
-correct_projdata uncorrect_projdata_simulation.par > my_create_prompts.log 2>&1
+INPUT=my_line_integrals${suffix}.hs OUTPUT=my_prompts${suffix}.hs \
+ MULT=my_acfs${suffix}.hs \
+ RANDOMS=my_randoms${suffix}.hs \
+ correct_projdata uncorrect_projdata.par > my_create_prompts${suffix}.log 2>&1
if [ $? -ne 0 ]; then
- echo "ERROR running correct_projdata. Check my_create_prompts.log"; exit 1;
+ echo "ERROR running correct_projdata. Check my_create_prompts${suffix}.log"; exit 1;
fi
# could call poisson_noise here
echo "=== create additive sinogram for reconstruction"
-# need randoms (and scatter) multiplied by ACF and norm (but we don't have a norm here)
-stir_math -s --mult my_additive_sinogram$suffix.hs my_randoms$suffix.hs my_acfs$suffix.hs
+# need randoms (and scatter) multiplied by ACF and norm (but we don't have a norm here yet)
+# need to use correct_projdata as in TOF, ACF/norm is non-TOF, so stir_math will fail
+INPUT=my_randoms${suffix}.hs OUTPUT=my_additive_sinogram${suffix}.hs \
+MULT=my_acfs${suffix}.hs \
+ correct_projdata correct_projdata_norm_only.par > my_create_additive_sino${suffix}.log 2>&1
if [ $? -ne 0 ]; then
- echo "ERROR running stir_math"; exit 1;
+ echo "ERROR running correct_projdata. Check my_create_additive_sino${suffix}.log"; exit 1;
fi
echo "Done creating simulated data"
diff --git a/recon_test_pack/template_for_ROOT_scanner.hs b/recon_test_pack/template_for_ROOT_scanner.hs
index 8dbf8861f..ea5939d87 100644
--- a/recon_test_pack/template_for_ROOT_scanner.hs
+++ b/recon_test_pack/template_for_ROOT_scanner.hs
@@ -2,7 +2,7 @@
!imaging modality := PT
name of data file := template_for_ROOT_scanner.s
originating system := ROOT_defined_scanner
-!version of keys := STIR3.0
+!version of keys := STIR6.0
!GENERAL DATA :=
patient orientation := head_in
patient rotation := supine
@@ -14,7 +14,9 @@ imagedata byte order := LITTLEENDIAN
applied corrections := {None}
!number format := float
!number of bytes per pixel := 4
-number of dimensions := 4
+number of dimensions := 5
+matrix axis label [5] := timing positions
+!matrix size [5] := 5
matrix axis label [4] := segment
!matrix size [4] := 7
matrix axis label [3] := view
@@ -26,23 +28,28 @@ matrix axis label [1] := tangential coordinate
minimum ring difference per segment := { -3,-2,-1,0,1,2,3}
maximum ring difference per segment := { -3,-2,-1,0,1,2,3}
Scanner parameters:=
-Scanner type := ROOT_demo_scanner
-Number of rings := 4
-Number of detectors per ring := 504
-Inner ring diameter (cm) := 65.6
-Average depth of interaction (cm) := 0.7
-Distance between rings (cm) := 0.40625
-Default bin size (cm) := 0.208626
-View offset (degrees) := 0
-Maximum number of non-arc-corrected bins := 344
-Default number of arc-corrected bins := 344
-Number of blocks per bucket in transaxial direction := 1
-Number of blocks per bucket in axial direction := 1
-Number of crystals per block in axial direction := 4
-Number of crystals per block in transaxial direction := 1
-Number of detector layers := 1
-Number of crystals per singles unit in axial direction := 4
-Number of crystals per singles unit in transaxial direction := 1
+ Scanner type := ROOT_demo_scanner
+ Number of rings := 4
+ Number of detectors per ring := 504
+ Inner ring diameter (cm) := 65.6
+ Average depth of interaction (cm) := 0.7
+ Distance between rings (cm) := 0.40625
+ Default bin size (cm) := 0.208626
+ View offset (degrees) := 0
+ Maximum number of non-arc-corrected bins := 501
+ Default number of arc-corrected bins := 501
+ Energy resolution := 0
+ Reference energy (in keV) := 511
+ Maximum number of (unmashed) TOF time bins := 5
+ Size of unmashed TOF time bins (ps) := 820
+ TOF timing resolution (ps) := 400
+ Number of blocks per bucket in transaxial direction := 1
+ Number of blocks per bucket in axial direction := 1
+ Number of crystals per block in axial direction := 4
+ Number of crystals per block in transaxial direction := 1
+ Number of detector layers := 1
+ Number of crystals per singles unit in axial direction := 4
+ Number of crystals per singles unit in transaxial direction := 1
end scanner parameters:=
effective central bin size (cm) := 0.208815
number of time frames := 1
diff --git a/recon_test_pack/test_image_3.ahv b/recon_test_pack/test_image_3.ahv
deleted file mode 100644
index 50167a1d3..000000000
--- a/recon_test_pack/test_image_3.ahv
+++ /dev/null
@@ -1,18 +0,0 @@
-!INTERFILE :=
-!name of data file := test_image_3.v
-!total number of images := 31
-!data offset in bytes := 0
-!imagedata byte order := LITTLEENDIAN
-!number format := short float
-!number of bytes per pixel := 4
-matrix axis label [1] := x
-!matrix size [1] := 97
-scaling factor (mm/pixel) [1] := 3.1088
-matrix axis label [2] := y
-!matrix size [2] := 97
-scaling factor (mm/pixel) [2] := 3.1088
-;Correct value is of keyword (commented out)
-;!slice thickness (pixels) := 1.08563
-;Value for Analyze
-!slice thickness (pixels) := 3.375
-!END OF INTERFILE :=
diff --git a/recon_test_pack/test_image_5.ahv b/recon_test_pack/test_image_5.ahv
deleted file mode 100644
index d2c3313b8..000000000
--- a/recon_test_pack/test_image_5.ahv
+++ /dev/null
@@ -1,18 +0,0 @@
-!INTERFILE :=
-!name of data file := test_image_5.v
-!total number of images := 31
-!data offset in bytes := 0
-!imagedata byte order := LITTLEENDIAN
-!number format := short float
-!number of bytes per pixel := 4
-matrix axis label [1] := x
-!matrix size [1] := 97
-scaling factor (mm/pixel) [1] := 3.1088
-matrix axis label [2] := y
-!matrix size [2] := 97
-scaling factor (mm/pixel) [2] := 3.1088
-;Correct value is of keyword (commented out)
-;!slice thickness (pixels) := 1.08563
-;Value for Analyze
-!slice thickness (pixels) := 3.375
-!END OF INTERFILE :=
diff --git a/recon_test_pack/test_image_PM_MRP_6.ahv b/recon_test_pack/test_image_PM_MRP_6.ahv
deleted file mode 100644
index 6b123ade3..000000000
--- a/recon_test_pack/test_image_PM_MRP_6.ahv
+++ /dev/null
@@ -1,18 +0,0 @@
-!INTERFILE :=
-!name of data file := test_image_PM_MRP_6.v
-!total number of images := 31
-!data offset in bytes := 0
-!imagedata byte order := LITTLEENDIAN
-!number format := short float
-!number of bytes per pixel := 4
-matrix axis label [1] := x
-!matrix size [1] := 60
-scaling factor (mm/pixel) [1] := 4.44114
-matrix axis label [2] := y
-!matrix size [2] := 60
-scaling factor (mm/pixel) [2] := 4.44114
-;Correct value is of keyword (commented out)
-;!slice thickness (pixels) := 0.759939
-;Value for Analyze
-!slice thickness (pixels) := 3.375
-!END OF INTERFILE :=
diff --git a/recon_test_pack/test_image_PM_QP_6.ahv b/recon_test_pack/test_image_PM_QP_6.ahv
deleted file mode 100644
index c8d9fe46c..000000000
--- a/recon_test_pack/test_image_PM_QP_6.ahv
+++ /dev/null
@@ -1,18 +0,0 @@
-!INTERFILE :=
-!name of data file := test_image_PM_QP_6.v
-!total number of images := 31
-!data offset in bytes := 0
-!imagedata byte order := LITTLEENDIAN
-!number format := short float
-!number of bytes per pixel := 4
-matrix axis label [1] := x
-!matrix size [1] := 60
-scaling factor (mm/pixel) [1] := 4.44114
-matrix axis label [2] := y
-!matrix size [2] := 60
-scaling factor (mm/pixel) [2] := 4.44114
-;Correct value is of keyword (commented out)
-;!slice thickness (pixels) := 0.75994
-;Value for Analyze
-!slice thickness (pixels) := 3.375
-!END OF INTERFILE :=
diff --git a/recon_test_pack/test_image_PM_QPweights_6.ahv b/recon_test_pack/test_image_PM_QPweights_6.ahv
deleted file mode 100644
index 1cb2f3439..000000000
--- a/recon_test_pack/test_image_PM_QPweights_6.ahv
+++ /dev/null
@@ -1,18 +0,0 @@
-!INTERFILE :=
-!name of data file := test_image_PM_QPweights_6.v
-!total number of images := 31
-!data offset in bytes := 0
-!imagedata byte order := LITTLEENDIAN
-!number format := short float
-!number of bytes per pixel := 4
-matrix axis label [1] := x
-!matrix size [1] := 60
-scaling factor (mm/pixel) [1] := 4.44114
-matrix axis label [2] := y
-!matrix size [2] := 60
-scaling factor (mm/pixel) [2] := 4.44114
-;Correct value is of keyword (commented out)
-;!slice thickness (pixels) := 0.75994
-;Value for Analyze
-!slice thickness (pixels) := 3.375
-!END OF INTERFILE :=
diff --git a/recon_test_pack/uncorrect_projdata.par b/recon_test_pack/uncorrect_projdata.par
new file mode 100644
index 000000000..679bd1437
--- /dev/null
+++ b/recon_test_pack/uncorrect_projdata.par
@@ -0,0 +1,14 @@
+correct_projdata Parameters :=
+
+ input file := ${INPUT}
+ output filename := ${OUTPUT}
+ ; use data (1) or set to one (0) :=
+ apply (1) or undo (0) correction := 0
+ randoms projdata filename := ${RANDOMS}
+
+ Bin Normalisation type := from projdata
+ Bin Normalisation From ProjData :=
+ normalisation projdata filename:= ${MULT}
+ End Bin Normalisation From ProjData:=
+
+END:=
diff --git a/recon_test_pack/uncorrect_projdata_simulation.par b/recon_test_pack/uncorrect_projdata_simulation.par
deleted file mode 100644
index e7f17fc78..000000000
--- a/recon_test_pack/uncorrect_projdata_simulation.par
+++ /dev/null
@@ -1,19 +0,0 @@
-correct_projdata Parameters :=
-
- input file := my_line_integrals${suffix}.hs
- output filename := my_prompts${suffix}.hs
- ; use data (1) or set to one (0) :=
- apply (1) or undo (0) correction := 0
- randoms projdata filename := my_randoms${suffix}.hs
-
- Bin Normalisation type := from projdata
- Bin Normalisation From ProjData :=
- ; only attenuation here for this simulation
- normalisation projdata filename:= my_acfs${suffix}.hs
- End Bin Normalisation From ProjData:=
-
- ; scatter term to be subtracted AFTER norm+atten correction
- ; defaults to 0
- ;scatter projdata filename := scatter${suffix}.hs
-
-END:=
diff --git a/scripts/plot_TOF_bins.m b/scripts/plot_TOF_bins.m
new file mode 100644
index 000000000..324e9ea7a
--- /dev/null
+++ b/scripts/plot_TOF_bins.m
@@ -0,0 +1,70 @@
+%%
+%Plot TOF bins.
+% Nikos Efthimiou. 2018/11/01
+% University of Hull
+
+%This scripts loads LOR files, exported by test_time_of_flight to the disk and plots them.
+%%
+clc; clear all;
+%Path to TOF files.
+path_name = './'
+
+pre_sort_files_in_path = dir(path_name)
+nums = []
+names = []
+
+for i = 1: size(pre_sort_files_in_path)
+ cur_file = pre_sort_files_in_path(i).name
+ if strfind (cur_file, 'glor')
+ num = sscanf(cur_file,'glor_%d')
+ % The following number can change accordingly.
+ if ((mod(num,1)==0) || num == 500000000)
+ nums{end+1} = int32(num);
+ names{end+1} = cur_file;
+ end
+
+ end
+end
+
+clear cur_file
+sorted_filenames = cell(numel(nums),2);
+[Sorted_A, Index_A] = sort(cell2mat(nums));
+sorted_filenames(:,2) = names(Index_A);
+
+% hold x values
+x_values = [];
+% hold the tof bins.
+y_tf_values = [];
+% hold the non tof LOR
+y__ntf_values = [];
+
+for i = 1 : size(sorted_filenames,1)
+ cur_file = sorted_filenames{i,2};
+
+ if strfind (cur_file, 'glor')
+
+ if strfind(cur_file, '500000000')
+ cur_full_path = fullfile(path_name, cur_file);
+
+ A = importdata(cur_full_path);
+ y_ntf_values = A(:,2);
+ else
+ cur_full_path = fullfile(path_name, cur_file);
+
+ A = importdata(cur_full_path);
+
+ if size(x_values) == 0
+ x_values = A(:,1);
+ end
+
+ y_tf_values = [y_tf_values A(:,2)];
+
+ end
+ end
+end
+
+sum_of_all_bins = sum(y_tf_values,2);
+x_v = x_values/0.299;
+
+%% Create Plot
+plot(x_v,y_tf_values(:,:), x_v, sum_of_all_bins, x_v, y_ntf_values)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a4a0f74d2..69748da97 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -74,7 +74,6 @@ else()
message(STATUS "HDF5 support disabled.")
endif()
-
if ((NOT DISABLE_ITK) AND ITK_FOUND)
message(STATUS "ITK libraries added.")
set(HAVE_ITK ON)
diff --git a/src/IO/GEHDF5Wrapper.cxx b/src/IO/GEHDF5Wrapper.cxx
index 28ef4c6b7..3142a7cb6 100644
--- a/src/IO/GEHDF5Wrapper.cxx
+++ b/src/IO/GEHDF5Wrapper.cxx
@@ -345,7 +345,6 @@ shared_ptr GEHDF5Wrapper::get_scanner_from_HDF5()
int num_rings = num_axial_blocks_per_bucket*num_axial_crystals_per_block*axial_modules_per_system;
int num_detectors_per_ring = num_transaxial_blocks_per_bucket*num_transaxial_crystals_per_block*radial_modules_per_system;
float ring_spacing = detector_axial_size/num_rings;
-
//PW Bin Size, default num of arc corrected bins and inner ring radius not found in RDF header.
// They will be set from the default STIR values
shared_ptr scanner_sptr(Scanner::get_scanner_from_name(read_str_scanner));
@@ -390,7 +389,6 @@ shared_ptr GEHDF5Wrapper::get_scanner_from_HDF5()
}
return scanner_sptr;
-
}
void GEHDF5Wrapper::initialise_proj_data_info_from_HDF5()
diff --git a/src/IO/IO_registries.cxx b/src/IO/IO_registries.cxx
index 0d193e36e..63f006168 100644
--- a/src/IO/IO_registries.cxx
+++ b/src/IO/IO_registries.cxx
@@ -56,7 +56,6 @@
#ifdef HAVE_HDF5
#include "stir/IO/GEHDF5ListmodeInputFileFormat.h"
#endif
-
//! Addition for SAFIR listmode input file format
#include "stir/IO/SAFIRCListmodeInputFileFormat.h"
diff --git a/src/IO/InputStreamFromROOTFile.cxx b/src/IO/InputStreamFromROOTFile.cxx
index 634c450f8..eaddbd251 100644
--- a/src/IO/InputStreamFromROOTFile.cxx
+++ b/src/IO/InputStreamFromROOTFile.cxx
@@ -11,6 +11,7 @@
* Copyright (C) 2015, 2016 University of Leeds
Copyright (C) 2016, 2020, 2021 UCL
Copyright (C) 2018 University of Hull
+
This file is part of STIR.
SPDX-License-Identifier: Apache-2.0
@@ -37,6 +38,7 @@ InputStreamFromROOTFile()
{
set_defaults();
reset();
+ least_significant_clock_bit = 1.0e+12; // TODO remove cst or rename
}
#if 0 // disabled as unused and incorrect
diff --git a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx
index 0bbcd2fb8..8314b7172 100644
--- a/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx
+++ b/src/IO/InputStreamFromROOTFileForCylindricalPET.cxx
@@ -52,7 +52,7 @@ InputStreamFromROOTFileForCylindricalPET(std::string _filename,
up_energy_window = _up_energy_window;
offset_dets = _offset_dets;
- half_block = module_repeater_y * submodule_repeater_y * crystal_repeater_y / 2 - 1;
+ half_block = module_repeater_y * submodule_repeater_y * crystal_repeater_y / 2 - 1;
if (half_block < 0 )
half_block = 0;
}
@@ -63,6 +63,7 @@ InputStreamFromROOTFileForCylindricalPET::
get_next_record(CListRecordROOT& record)
{
int ring1, ring2, crystal1, crystal2;
+ double delta_timing_bin;
bool eof = false;
#ifdef STIR_OPENMP
@@ -132,6 +133,8 @@ get_next_record(CListRecordROOT& record)
crystal1 += offset_dets;
crystal2 += offset_dets;
#endif
+
+ delta_timing_bin = (time2 - time1) * least_significant_clock_bit;
}
if(eof)
@@ -140,7 +143,7 @@ get_next_record(CListRecordROOT& record)
return
record.init_from_data(ring1, ring2,
crystal1, crystal2,
- time1, time2,
+ time1, delta_timing_bin,
eventID1, eventID2);
}
diff --git a/src/IO/InputStreamFromROOTFileForECATPET.cxx b/src/IO/InputStreamFromROOTFileForECATPET.cxx
index 51843c99a..83c9901b6 100644
--- a/src/IO/InputStreamFromROOTFileForECATPET.cxx
+++ b/src/IO/InputStreamFromROOTFileForECATPET.cxx
@@ -62,6 +62,7 @@ get_next_record(CListRecordROOT& record)
{
int ring1, ring2, crystal1, crystal2;
+ double delta_timing_bin;
bool return_no = false;
#ifdef STIR_OPENMP
@@ -115,6 +116,7 @@ get_next_record(CListRecordROOT& record)
crystal2 += offset_dets;
#endif
+ delta_timing_bin = (time2 - time1) * least_significant_clock_bit;
}
if(return_no)
@@ -123,7 +125,7 @@ get_next_record(CListRecordROOT& record)
return
record.init_from_data(ring1, ring2,
crystal1, crystal2,
- time1, time2,
+ time1, delta_timing_bin,
eventID1, eventID2);
}
diff --git a/src/IO/InterfileHeader.cxx b/src/IO/InterfileHeader.cxx
index 1cb191609..b5c525297 100644
--- a/src/IO/InterfileHeader.cxx
+++ b/src/IO/InterfileHeader.cxx
@@ -2,7 +2,7 @@
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000 - 2009-04-30, Hammersmith Imanet Ltd
Copyright (C) 2011-07-01 - 2012, Kris Thielemans
- Copyright (C) 2013, 2016, 2018, 2020 University College London
+ Copyright (C) 2013, 2016, 2018, 2020, 2023 University College London
Copyright 2017 ETH Zurich, Institute of Particle Physics and Astrophysics
This file is part of STIR.
@@ -32,6 +32,7 @@
#include "stir/info.h"
#include "stir/warning.h"
#include "stir/error.h"
+#include
#include
#include
#include "stir/ProjDataInfoBlocksOnCylindricalNoArcCorr.h"
@@ -545,7 +546,14 @@ InterfilePDFSHeader::InterfilePDFSHeader()
(KeywordProcessor)&InterfilePDFSHeader::resize_segments_and_set,
&max_ring_difference);
-
+ tof_mash_factor = 1;
+ add_key("TOF mashing factor",
+ &tof_mash_factor);
+#if STIR_VERSION < 070000
+ add_alias_key("TOF mashing factor", "%TOF mashing factor");
+#endif
+
+ // Scanner keys
// warning these keys should match what is in Scanner::parameter_info()
// TODO get Scanner to parse these
ignore_key("Scanner parameters");
@@ -614,6 +622,25 @@ InterfilePDFSHeader::InterfilePDFSHeader()
add_key("Reference energy (in keV)",
&reference_energy);
+ max_num_timing_poss = -1;
+ add_key("Maximum number of (unmashed) TOF time bins",
+ &max_num_timing_poss);
+#if STIR_VERSION < 070000
+ add_alias_key("Maximum number of (unmashed) TOF time bins", "Number of TOF time bins");
+#endif
+ size_of_timing_pos = -1.f;
+ add_key("Size of unmashed TOF time bins (ps)",
+ &size_of_timing_pos);
+#if STIR_VERSION < 070000
+ add_alias_key("Size of unmashed TOF time bins (ps)", "Size of timing bin (ps)");
+#endif
+ timing_resolution = -1.f;
+ add_key("TOF timing resolution (ps)",
+ &timing_resolution);
+#if STIR_VERSION < 070000
+ add_alias_key("TOF timing resolution (ps)", "timing resolution (ps)");
+#endif
+
// new keys for block geometry
scanner_geometry = "Cylindrical";
add_key("Scanner geometry (BlocksOnCylindrical/Cylindrical/Generic)",
@@ -675,13 +702,21 @@ int InterfilePDFSHeader::find_storage_order()
}
*/
- if (num_dimensions != 4)
+ if (num_dimensions != 4 &&
+ num_dimensions != 5)
{
- warning("Interfile error: expecting 4D structure ");
+ warning("Interfile error: expecting 4D structure or 5D in case of TOF information ");
stop_parsing();
return true;
}
+ if (num_dimensions == 4)
+ {
+ // non-TOF
+ num_timing_poss = 1;
+ tof_mash_factor = 0;
+ }
+
if (matrix_labels[0] != "tangential coordinate")
{
// use error message with index [1] as that is what the user sees.
@@ -694,16 +729,36 @@ int InterfilePDFSHeader::find_storage_order()
if (matrix_labels[3] == "segment")
{
num_segments = matrix_size[3][0];
-
+
if (matrix_labels[1] == "axial coordinate" && matrix_labels[2] == "view")
{
- storage_order =ProjDataFromStream::Segment_View_AxialPos_TangPos;
- num_views = matrix_size[2][0];
+ // If TOF information is in there
+ if (matrix_labels.size() > 4)
+ {
+ if (matrix_labels[4] == "timing positions")
+ {
+ num_timing_poss = matrix_size[4][0];
+ storage_order = ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos;
+ num_views = matrix_size[2][0];
#ifdef _MSC_VER
- num_rings_per_segment.assign(matrix_size[1].begin(), matrix_size[1].end());
+ num_rings_per_segment.assign(matrix_size[1].begin(), matrix_size[1].end());
#else
- num_rings_per_segment = matrix_size[1];
+ num_rings_per_segment = matrix_size[1];
#endif
+ }
+ else
+ error("Interfile header parsing: currently need 'matrix axis label [5] := timing positions' for TOF data");
+ }
+ else
+ {
+ storage_order = ProjDataFromStream::Segment_View_AxialPos_TangPos;
+ num_views = matrix_size[2][0];
+#ifdef _MSC_VER
+ num_rings_per_segment.assign(matrix_size[1].begin(), matrix_size[1].end());
+#else
+ num_rings_per_segment = matrix_size[1];
+#endif
+ }
}
else if (matrix_labels[1] == "view" && matrix_labels[2] == "axial coordinate")
{
@@ -924,6 +979,7 @@ find_segment_sequence(vector& segment_sequence,
}
// MJ 17/05/2000 made bool
+// NE 28/12/2016 Accounts for TOF stuff.
bool InterfilePDFSHeader::post_processing()
{
@@ -1308,6 +1364,28 @@ bool InterfilePDFSHeader::post_processing()
}
// end of new variables for block geometry
+ if (guessed_scanner_ptr->is_tof_ready())
+ {
+ if (max_num_timing_poss != guessed_scanner_ptr->get_max_num_timing_poss())
+ {
+ warning(boost::format("Interfile warning: 'Maximum number of (unmashed) TOF time bins' (%d) is expected to be %d.") %
+ max_num_timing_poss % guessed_scanner_ptr->get_max_num_timing_poss());
+ mismatch_between_header_and_guess = true;
+ }
+ if (abs(size_of_timing_pos - guessed_scanner_ptr->get_size_of_timing_pos()) > 0.001F)
+ {
+ warning(boost::format("Interfile warning: 'Size of unmashed TOF timing bin (ps)' (%f) is expected to be %f.") %
+ size_of_timing_pos % guessed_scanner_ptr->get_size_of_timing_pos());
+ mismatch_between_header_and_guess = true;
+ }
+ if (abs(timing_resolution - guessed_scanner_ptr->get_timing_resolution()) > 0.01F)
+ {
+ warning(boost::format("Interfile warning: 'TOF timing resolution (ps)' (%f) is expected to be %f.") %
+ timing_resolution % guessed_scanner_ptr->get_timing_resolution());
+ mismatch_between_header_and_guess = true;
+ }
+ }
+
// end of checks. If they failed, we ignore the guess
if (mismatch_between_header_and_guess)
{
@@ -1359,27 +1437,31 @@ bool InterfilePDFSHeader::post_processing()
// finally, we construct a new scanner object with
// data from the Interfile header (or the guessed scanner).
- shared_ptr scanner_ptr_from_file(
+
+ shared_ptr scanner_sptr_from_file(
new Scanner(guessed_scanner_ptr->get_type(),
- get_exam_info().originating_system,
- num_detectors_per_ring,
- num_rings,
- max_num_non_arccorrected_bins,
- default_num_arccorrected_bins,
- static_cast(inner_ring_diameter_in_cm*10./2),
+ get_exam_info_sptr()->originating_system,
+ num_detectors_per_ring,
+ num_rings,
+ max_num_non_arccorrected_bins,
+ default_num_arccorrected_bins,
+ static_cast(inner_ring_diameter_in_cm*10./2),
static_cast(average_depth_of_interaction_in_cm*10),
- static_cast(distance_between_rings_in_cm*10.),
- static_cast(default_bin_size_in_cm*10),
- static_cast(view_offset_in_degrees*_PI/180),
- num_axial_blocks_per_bucket,
- num_transaxial_blocks_per_bucket,
- num_axial_crystals_per_block,
- num_transaxial_crystals_per_block,
- num_axial_crystals_per_singles_unit,
+ static_cast(distance_between_rings_in_cm*10.),
+ static_cast(default_bin_size_in_cm*10),
+ static_cast(view_offset_in_degrees*_PI/180),
+ num_axial_blocks_per_bucket,
+ num_transaxial_blocks_per_bucket,
+ num_axial_crystals_per_block,
+ num_transaxial_crystals_per_block,
+ num_axial_crystals_per_singles_unit,
num_transaxial_crystals_per_singles_unit,
num_detector_layers,
energy_resolution,
reference_energy,
+ max_num_timing_poss,
+ size_of_timing_pos,
+ timing_resolution,
scanner_geometry,
static_cast(axial_distance_between_crystals_in_cm*10.),
static_cast(transaxial_distance_between_crystals_in_cm*10.),
@@ -1389,54 +1471,52 @@ bool InterfilePDFSHeader::post_processing()
));
bool is_consistent =
- scanner_ptr_from_file->check_consistency() == Succeeded::yes;
- if (scanner_ptr_from_file->get_type() == Scanner::Unknown_scanner ||
- scanner_ptr_from_file->get_type() == Scanner::User_defined_scanner ||
+ scanner_sptr_from_file->check_consistency() == Succeeded::yes;
+ if (scanner_sptr_from_file->get_type() == Scanner::Unknown_scanner ||
+ scanner_sptr_from_file->get_type() == Scanner::User_defined_scanner ||
mismatch_between_header_and_guess ||
!is_consistent)
{
warning(boost::format("Interfile parsing ended up with the following scanner:\n%s\n") %
- scanner_ptr_from_file->parameter_info().c_str());
+ scanner_sptr_from_file->parameter_info());
}
// float azimuthal_angle_sampling =_PI/num_views;
-
-
- if (scanner_geometry == "Cylindrical")
- {
+ if (scanner_geometry == "Cylindrical")
+ {
if (is_arccorrected)
{
if (effective_central_bin_size_in_cm <= 0)
- effective_central_bin_size_in_cm =
- scanner_ptr_from_file->get_default_bin_size()/10;
- else if (fabs(effective_central_bin_size_in_cm -
- scanner_ptr_from_file->get_default_bin_size()/10)>.001)
+ effective_central_bin_size_in_cm =
+ scanner_sptr_from_file->get_default_bin_size()/10;
+ else if (fabs(effective_central_bin_size_in_cm -
+ scanner_sptr_from_file->get_default_bin_size()/10)>.001)
warning(boost::format("Interfile warning: unexpected effective_central_bin_size_in_cm\n"
"Value in header is %g while the default for the scanner is %g\n"
"Using value from header.") %
effective_central_bin_size_in_cm %
- (scanner_ptr_from_file->get_default_bin_size()/10));
+ (scanner_sptr_from_file->get_default_bin_size()/10));
data_info_sptr.reset(
new ProjDataInfoCylindricalArcCorr (
- scanner_ptr_from_file,
+ scanner_sptr_from_file,
float(effective_central_bin_size_in_cm*10.),
sorted_num_rings_per_segment,
sorted_min_ring_diff,
sorted_max_ring_diff,
- num_views,num_bins));
+ num_views,num_bins, tof_mash_factor));
}
else
{
data_info_sptr.reset(
new ProjDataInfoCylindricalNoArcCorr (
- scanner_ptr_from_file,
+ scanner_sptr_from_file,
sorted_num_rings_per_segment,
sorted_min_ring_diff,
sorted_max_ring_diff,
- num_views,num_bins));
+ num_views,num_bins, tof_mash_factor));
if (effective_central_bin_size_in_cm>0 &&
fabs(effective_central_bin_size_in_cm -
data_info_sptr->get_sampling_in_s(Bin(0,0,0,0))/10.)>.01)
@@ -1453,7 +1533,7 @@ bool InterfilePDFSHeader::post_processing()
{
data_info_sptr.reset(
new ProjDataInfoBlocksOnCylindricalNoArcCorr (
- scanner_ptr_from_file,
+ scanner_sptr_from_file,
sorted_num_rings_per_segment,
sorted_min_ring_diff,
sorted_max_ring_diff,
@@ -1473,7 +1553,7 @@ bool InterfilePDFSHeader::post_processing()
{
data_info_sptr.reset(
new ProjDataInfoGenericNoArcCorr (
- scanner_ptr_from_file,
+ scanner_sptr_from_file,
sorted_num_rings_per_segment,
sorted_min_ring_diff,
sorted_max_ring_diff,
@@ -1489,6 +1569,11 @@ bool InterfilePDFSHeader::post_processing()
(data_info_sptr->get_sampling_in_s(Bin(0,0,0,0))/10.));
}
}
+ if (data_info_sptr->get_num_tof_poss() != num_timing_poss)
+ error(boost::format("Interfile header parsing with TOF: inconsistency between number of TOF bins in data (%d), "
+ "TOF mashing factor (%d) and max number of TOF bins in scanner info (%d)")
+ % num_timing_poss % tof_mash_factor % scanner_sptr_from_file->get_max_num_timing_poss());
+
//cerr << data_info_sptr->parameter_info() << endl;
// Set the bed position
diff --git a/src/IO/InterfileHeaderSiemens.cxx b/src/IO/InterfileHeaderSiemens.cxx
index 1c7e37bb0..696be0d08 100644
--- a/src/IO/InterfileHeaderSiemens.cxx
+++ b/src/IO/InterfileHeaderSiemens.cxx
@@ -203,6 +203,8 @@ InterfileRawDataHeaderSiemens::InterfileRawDataHeaderSiemens()
num_rings = -1;
maximum_ring_difference = -1;
axial_compression = -1;
+ tof_mash_factor = -1;
+ num_tof_bins = 1;
add_key("number of rings", &num_rings);
add_key("%axial compression", &axial_compression);
@@ -219,6 +221,7 @@ InterfileRawDataHeaderSiemens::InterfileRawDataHeaderSiemens()
add_key("PET data type",
&PET_data_type_index,
&PET_data_type_values);
+ add_key("%tof mashing factor", &tof_mash_factor);
// TODO should add data format:=CoincidenceList|sinogram and then check its value
remove_key("process status");
@@ -291,11 +294,24 @@ bool InterfileRawDataHeaderSiemens::post_processing()
num_rings, scanner_sptr->get_num_rings());
}
+ if (tof_mash_factor < 0) // check if it was not set yet
+ {
+ switch (scanner_sptr->get_type())
+ {
+ case Scanner::Siemens_Vision_600:
+ tof_mash_factor = 8;
+ break;
+ default:
+ tof_mash_factor = 1;
+ }
+ warning("TOF mashing factor was not set. Using " + std::to_string(tof_mash_factor));
+ }
+
data_info_ptr =
ProjDataInfo::construct_proj_data_info(scanner_sptr,
axial_compression, maximum_ring_difference,
num_views, num_bins,
- is_arccorrected);
+ is_arccorrected, tof_mash_factor);
// handle segments
{
@@ -339,8 +355,6 @@ InterfilePDFSHeaderSiemens::InterfilePDFSHeaderSiemens()
ignore_key("%sinogram type"); // value: "step and shoot"
ignore_key("scale factor (degree/pixel)");
- ignore_key("%tof mashing factor");
- // add_key(%tof mashing factor", &tof_mashing_factor);
ignore_key("total number of data sets");
add_key("%number of buckets",
@@ -370,32 +384,46 @@ void InterfilePDFSHeaderSiemens::read_bucket_singles_rates()
int InterfilePDFSHeaderSiemens::find_storage_order()
{
- if (num_dimensions != 3)
+ if (num_dimensions != 3 && num_dimensions != 4)
{
- warning("Interfile error: expecting 3D data ");
+ warning("Interfile error: expecting 3D or 4D data ");
stop_parsing();
return true;
}
if ((matrix_size[0].size() != 1) ||
- (matrix_size[1].size() != 1) ||
- (matrix_size[2].size() != 1))
+ (matrix_size[1].size() != 1) ||
+ (matrix_size[2].size() != 1) ||
+ (matrix_size[num_dimensions-1].size() != 1))
{
- error("Interfile error: strange values for the matrix_size keyword(s)");
+ error("Siemens Interfile error: strange values for the matrix_size keyword(s)");
}
- if (matrix_labels[0] != "bin" && matrix_labels[0] != "x") // x is used for arccorrected data (ACF)
+ if (matrix_labels[0] != "bin" && matrix_labels[0] != "x" && matrix_labels[0] != "sinogram projections") // x is used for arccorrected data (ACF)
{
// use error message with index [1] as that is what the user sees.
- error("Interfile error: expecting 'matrix axis label[1] := bin' or 'x'");
+ error("Siemens Interfile error: expecting 'matrix axis label[1] := bin' or 'x' or 'sinogram projections'");
}
num_bins = matrix_size[0][0];
- if ((matrix_labels[1] == "projection" && matrix_labels[2] == "plane") || // used for emission
- (matrix_labels[1] == "sinogram views" && matrix_labels[2] == "number of sinograms") // used for ACF
+ if (num_dimensions == 3 &&
+ ((matrix_labels[1] == "projection" && matrix_labels[2] == "plane") || // used for emission
+ (matrix_labels[1] == "sinogram views" && matrix_labels[2] == "number of sinograms") // used for ACF
+ )
)
{
- storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos;
- num_views = matrix_size[1][0];
+ if (num_tof_bins>1)
+ storage_order = ProjDataFromStream::Timing_Segment_AxialPos_View_TangPos;
+ else
+ storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos;
+ num_views = matrix_size[1][0];
+ }
+ else if (num_dimensions == 4 &&
+ matrix_labels[1] == "sinogram views" && matrix_labels[2] == "number of sinograms" &&
+ matrix_labels[3] == "TOF bin") // used for TOF
+ {
+ storage_order = ProjDataFromStream::Timing_Segment_AxialPos_View_TangPos;
+ num_views = matrix_size[1][0];
+ num_tof_bins = matrix_size[3][0];
}
else
{
@@ -446,6 +474,12 @@ bool InterfilePDFSHeaderSiemens::post_processing()
if (InterfileRawDataHeaderSiemens::post_processing() == true)
return true;
+ // handle TOF index order
+ if (this->data_info_ptr->get_num_tof_poss() > 1)
+ {
+ this->timing_poss_sequence = ecat::find_timing_poss_sequence(*this->data_info_ptr);
+ }
+
compression = (standardise_interfile_keyword(compression_as_string) == "on");
return false;
@@ -527,8 +561,10 @@ InterfileListmodeHeaderSiemens::InterfileListmodeHeaderSiemens()
int InterfileListmodeHeaderSiemens::find_storage_order()
{
- // always...
- storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos;
+ if (num_tof_bins>1)
+ storage_order = ProjDataFromStream::Timing_Segment_AxialPos_View_TangPos;
+ else
+ storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos;
return false;
}
diff --git a/src/IO/InterfilePDFSHeaderSPECT.cxx b/src/IO/InterfilePDFSHeaderSPECT.cxx
index 2066ae118..c72e71840 100644
--- a/src/IO/InterfilePDFSHeaderSPECT.cxx
+++ b/src/IO/InterfilePDFSHeaderSPECT.cxx
@@ -155,27 +155,37 @@ bool InterfilePDFSHeaderSPECT::post_processing()
const int num_axial_crystals_per_singles_unit = -1;
const int num_transaxial_crystals_per_singles_unit = -1;
const int num_detector_layers = 1;
+ const float energy_resolution = -1.f;
+ const float reference_energy = -1.f;
+ const short int max_num_of_timing_poss = 1;
+ const float size_timing_pos = -1.f;
+ const float timing_resolution = -1.f;
shared_ptr guessed_scanner_ptr(Scanner::get_scanner_from_name(get_exam_info().originating_system));
shared_ptr scanner_ptr_from_file(
- new Scanner(guessed_scanner_ptr->get_type(),
- get_exam_info().originating_system,
- num_detectors_per_ring,
- num_rings,
- max_num_non_arccorrected_bins,
- default_num_arccorrected_bins,
- static_cast(radii[0]),
- static_cast(average_depth_of_interaction_in_cm*10),
- static_cast(distance_between_rings_in_cm*10.),
- static_cast(default_bin_size_in_cm*10),
- static_cast(view_offset_in_degrees*_PI/180),
- num_axial_blocks_per_bucket,
- num_transaxial_blocks_per_bucket,
- num_axial_crystals_per_block,
- num_transaxial_crystals_per_block,
- num_axial_crystals_per_singles_unit,
- num_transaxial_crystals_per_singles_unit,
- num_detector_layers));
+ new Scanner(guessed_scanner_ptr->get_type(),
+ get_exam_info_sptr()->originating_system,
+ num_detectors_per_ring,
+ num_rings,
+ max_num_non_arccorrected_bins,
+ default_num_arccorrected_bins,
+ static_cast(radii[0]),
+ static_cast(average_depth_of_interaction_in_cm*10),
+ static_cast(distance_between_rings_in_cm*10.),
+ static_cast(default_bin_size_in_cm*10),
+ static_cast(view_offset_in_degrees*_PI/180),
+ num_axial_blocks_per_bucket,
+ num_transaxial_blocks_per_bucket,
+ num_axial_crystals_per_block,
+ num_transaxial_crystals_per_block,
+ num_axial_crystals_per_singles_unit,
+ num_transaxial_crystals_per_singles_unit,
+ num_detector_layers,
+ energy_resolution,
+ reference_energy,
+ max_num_of_timing_poss,
+ size_timing_pos,
+ timing_resolution));
#if 0
if (default_bin_size_in_cm <= 0)
default_bin_size_in_cm =
diff --git a/src/IO/interfile.cxx b/src/IO/interfile.cxx
index fb0f824f2..0f7aaad1f 100644
--- a/src/IO/interfile.cxx
+++ b/src/IO/interfile.cxx
@@ -77,7 +77,7 @@ is_interfile_signature(const char * const signature)
{
// checking for "interfile :"
const char * pos_of_colon = strchr(signature, ':');
- if (pos_of_colon == NULL)
+ if (pos_of_colon == 0)
return false;
string keyword(signature, pos_of_colon-signature);
return (
@@ -568,9 +568,9 @@ write_basic_interfile_image_header(const string& header_file_name,
if (is_spect)
output_header << "!version of keys := 3.3\n";
else
- output_header << "!version of keys := STIR4.0\n";
+ output_header << "!version of keys := STIR6.0\n";
#else
- output_header << "!version of keys := STIR4.0\n";
+ output_header << "!version of keys := STIR6.0\n";
#endif
output_header << "name of data file := " << data_file_name_in_header << endl;
@@ -1059,7 +1059,7 @@ read_interfile_PDFS_Siemens(istream& input,
if (hdr.compression)
warning("Siemens projection data is compressed. Reading of raw data will fail.");
- return new ProjDataFromStream(hdr.get_exam_info_sptr(),
+ auto pdfs_ptr = new ProjDataFromStream(hdr.get_exam_info_sptr(),
hdr.data_info_ptr->create_shared_clone(),
data_in,
hdr.data_offset_each_dataset[0],
@@ -1069,6 +1069,9 @@ read_interfile_PDFS_Siemens(istream& input,
hdr.file_byte_order,
1.);
+ if (hdr.timing_poss_sequence.size() > 1)
+ pdfs_ptr->set_timing_poss_sequence_in_stream(hdr.timing_poss_sequence);
+ return pdfs_ptr;
}
ProjDataFromStream*
@@ -1132,7 +1135,7 @@ read_interfile_PDFS(istream& input,
return 0;
}
- return new ProjDataFromStream(hdr.get_exam_info_sptr(),
+ auto pdfs_ptr = new ProjDataFromStream(hdr.get_exam_info_sptr(),
hdr.data_info_sptr->create_shared_clone(),
data_in,
hdr.data_offset_each_dataset[0],
@@ -1142,7 +1145,9 @@ read_interfile_PDFS(istream& input,
hdr.file_byte_order,
static_cast(hdr.image_scaling_factors[0][0]));
-
+ if (hdr.timing_poss_sequence.size() > 1)
+ pdfs_ptr->set_timing_poss_sequence_in_stream(hdr.timing_poss_sequence);
+ return pdfs_ptr;
}
@@ -1204,7 +1209,7 @@ write_basic_interfile_PDFS_header(const string& header_file_name,
if (is_spect)
output_header << "!version of keys := 3.3\n";
else
- output_header << "!version of keys := STIR4.0\n";
+ output_header << "!version of keys := STIR6.0\n";
output_header << "!GENERAL DATA :=\n";
output_header << "!GENERAL IMAGE DATA :=\n";
@@ -1296,53 +1301,71 @@ write_basic_interfile_PDFS_header(const string& header_file_name,
}
// it's PET data if we get here
+ // N.E. Added timing locations
+ const bool is_TOF = pdfs.get_proj_data_info_sptr()->get_num_tof_poss()>1;
+ output_header << "number of dimensions := " + std::to_string(is_TOF ? 5: 4) + "\n";
- output_header << "number of dimensions := 4\n";
-
- // TODO support more ?
+ // TODO support more ?
{
// default to Segment_View_AxialPos_TangPos
int order_of_segment = 4;
int order_of_view = 3;
int order_of_z = 2;
int order_of_bin = 1;
+ int order_of_timing_poss = 0;
switch(pdfs.get_storage_order())
/*
- {
+ {
case ProjDataFromStream::ViewSegmentRingBin:
- {
- order_of_segment = 2;
- order_of_view = 1;
- order_of_z = 3;
- break;
- }
- */
+ {
+ order_of_segment = 2;
+ order_of_view = 1;
+ order_of_z = 3;
+ break;
+ }
+ */
{
case ProjDataFromStream::Segment_View_AxialPos_TangPos:
- {
- order_of_segment = 4;
- order_of_view = 3;
- order_of_z = 2;
- break;
- }
+ {
+ order_of_segment = 4;
+ order_of_view = 3;
+ order_of_z = 2;
+ break;
+ }
case ProjDataFromStream::Segment_AxialPos_View_TangPos:
- {
- order_of_segment = 4;
- order_of_view = 2;
- order_of_z = 3;
- break;
- }
+ {
+ order_of_segment = 4;
+ order_of_view = 2;
+ order_of_z = 3;
+ break;
+ }
+ case ProjDataFromStream::Timing_Segment_View_AxialPos_TangPos:
+ {
+ order_of_timing_poss = 5;
+ order_of_segment = 4;
+ order_of_view = 3;
+ order_of_z = 2;
+ break;
+ }
default:
- {
- error("write_interfile_PSOV_header: unsupported storage order, "
+ {
+ error("write_interfile_PSOV_header: unsupported storage order, "
"defaulting to Segment_View_AxialPos_TangPos.\n Please correct by hand !");
- }
+ }
}
-
- output_header << "matrix axis label [" << order_of_segment
- << "] := segment\n";
- output_header << "!matrix size [" << order_of_segment << "] := "
- << pdfs.get_segment_sequence_in_stream().size()<< "\n";
+
+ if (order_of_timing_poss > 0)
+ {
+ output_header << "matrix axis label [" << order_of_timing_poss
+ << "] := timing positions\n";
+ output_header << "!matrix size [" << order_of_timing_poss << "] := "
+ << pdfs.get_timing_poss_sequence_in_stream().size()<< "\n";
+ }
+
+ output_header << "matrix axis label [" << order_of_segment
+ << "] := segment\n";
+ output_header << "!matrix size [" << order_of_segment << "] := "
+ << pdfs.get_segment_sequence_in_stream().size()<< "\n";
output_header << "matrix axis label [" << order_of_view << "] := view\n";
output_header << "!matrix size [" << order_of_view << "] := "
<< pdfs.get_proj_data_info_sptr()->get_num_views() << "\n";
@@ -1354,13 +1377,19 @@ write_basic_interfile_PDFS_header(const string& header_file_name,
std::vector::const_iterator seg = segment_sequence.begin();
output_header << "{ " <get_num_axial_poss(*seg);
for (seg++; seg != segment_sequence.end(); seg++)
- output_header << "," << pdfs.get_proj_data_info_sptr()->get_num_axial_poss(*seg);
+ output_header << "," << pdfs.get_proj_data_info_sptr()->get_num_axial_poss(*seg);
output_header << "}\n";
}
output_header << "matrix axis label [" << order_of_bin << "] := tangential coordinate\n";
output_header << "!matrix size [" << order_of_bin << "] := "
- <get_num_tangential_poss() << "\n";
+ <get_num_tangential_poss() << "\n";
+
+ if (is_TOF)
+ {
+ output_header << "TOF mashing factor := " <<
+ pdfs.get_proj_data_info_sptr()->get_tof_mash_factor() << "\n";
+ }
}
const shared_ptr proj_data_info_sptr =
@@ -1369,8 +1398,8 @@ write_basic_interfile_PDFS_header(const string& header_file_name,
if (!is_null_ptr(proj_data_info_sptr))
{
// cylindrical scanners
-
- output_header << "minimum ring difference per segment := ";
+
+ output_header << "minimum ring difference per segment := ";
{
std::vector::const_iterator seg = segment_sequence.begin();
output_header << "{ " << proj_data_info_sptr->get_min_ring_difference(*seg);
diff --git a/src/IO/stir_ecat_common.cxx b/src/IO/stir_ecat_common.cxx
index b565656fa..4f21b7e68 100644
--- a/src/IO/stir_ecat_common.cxx
+++ b/src/IO/stir_ecat_common.cxx
@@ -13,7 +13,7 @@
/*
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000- 2009, Hammersmith Imanet Ltd
- Copyright (C) 2020, University College London
+ Copyright (C) 2020, 2023 University College London
This file is part of STIR.
SPDX-License-Identifier: Apache-2.0 AND License-ref-PARAPET-license
@@ -199,5 +199,20 @@ find_segment_sequence(const ProjDataInfo& pdi)
return segment_sequence;
}
+std::vector
+find_timing_poss_sequence(const ProjDataInfo& pdi)
+{
+ const int max_timing_pos_num = pdi.get_num_tof_poss()/2;
+ std::vector timing_pos_sequence(2*max_timing_pos_num+1);
+ // Siemens always stores timing_poss as 0, -1, +1, ...
+ timing_pos_sequence[0] = 0;
+ for (int timing_pos_num = 1; timing_pos_num<=max_timing_pos_num; ++timing_pos_num)
+ {
+ timing_pos_sequence[2*timing_pos_num-1] = timing_pos_num;
+ timing_pos_sequence[2*timing_pos_num] = -timing_pos_num;
+ }
+ return timing_pos_sequence;
+}
+
END_NAMESPACE_ECAT
END_NAMESPACE_STIR
diff --git a/src/buildblock/ArcCorrection.cxx b/src/buildblock/ArcCorrection.cxx
index 979ada5cd..5f8734437 100644
--- a/src/buildblock/ArcCorrection.cxx
+++ b/src/buildblock/ArcCorrection.cxx
@@ -244,6 +244,7 @@ do_arc_correction(Sinogram& out, const Sinogram& in) const
assert(*out.get_proj_data_info_sptr() == *_arc_corr_proj_data_info_sptr);
assert(out.get_axial_pos_num() == in.get_axial_pos_num());
assert(out.get_segment_num() == in.get_segment_num());
+ assert(out.get_timing_pos_num() == in.get_timing_pos_num());
for (int view_num=in.get_min_view_num(); view_num<=in.get_max_view_num(); ++view_num)
do_arc_correction(out[view_num], in[view_num]);
}
@@ -254,7 +255,8 @@ do_arc_correction(const Sinogram& in) const
{
Sinogram out(_arc_corr_proj_data_info_sptr,
in.get_axial_pos_num(),
- in.get_segment_num());
+ in.get_segment_num(),
+ in.get_timing_pos_num());
do_arc_correction(out, in);
return out;
}
@@ -267,6 +269,7 @@ do_arc_correction(Viewgram& out, const Viewgram& in) const
assert(*out.get_proj_data_info_sptr() == *_arc_corr_proj_data_info_sptr);
assert(out.get_view_num() == in.get_view_num());
assert(out.get_segment_num() == in.get_segment_num());
+ assert(out.get_timing_pos_num() == in.get_timing_pos_num());
for (int axial_pos_num=in.get_min_axial_pos_num(); axial_pos_num<=in.get_max_axial_pos_num(); ++axial_pos_num)
do_arc_correction(out[axial_pos_num], in[axial_pos_num]);
}
@@ -277,7 +280,8 @@ do_arc_correction(const Viewgram& in) const
{
Viewgram out(_arc_corr_proj_data_info_sptr,
in.get_view_num(),
- in.get_segment_num());
+ in.get_segment_num(),
+ in.get_timing_pos_num());
do_arc_correction(out, in);
return out;
}
@@ -298,7 +302,7 @@ do_arc_correction(const RelatedViewgrams& in) const
{
RelatedViewgrams out =
_arc_corr_proj_data_info_sptr->get_empty_related_viewgrams(in.get_basic_view_segment_num(),
- in.get_symmetries_sptr());
+ in.get_symmetries_sptr(), false, in.get_basic_timing_pos_num());
do_arc_correction(out, in);
return out;
}
@@ -310,6 +314,7 @@ do_arc_correction(SegmentBySinogram& out, const SegmentBySinogram&
assert(*in.get_proj_data_info_sptr() == *_noarc_corr_proj_data_info_sptr);
assert(*out.get_proj_data_info_sptr() == *_arc_corr_proj_data_info_sptr);
assert(out.get_segment_num() == in.get_segment_num());
+ assert(out.get_timing_pos_num() == in.get_timing_pos_num());
for (int axial_pos_num=in.get_min_axial_pos_num(); axial_pos_num<=in.get_max_axial_pos_num(); ++axial_pos_num)
for (int view_num=in.get_min_view_num(); view_num<=in.get_max_view_num(); ++view_num)
do_arc_correction(out[axial_pos_num][view_num], in[axial_pos_num][view_num]);
@@ -320,7 +325,7 @@ ArcCorrection::
do_arc_correction(const SegmentBySinogram& in) const
{
SegmentBySinogram out(_arc_corr_proj_data_info_sptr,
- in.get_segment_num());
+ in.get_segment_num(), in.get_timing_pos_num());
do_arc_correction(out, in);
return out;
}
@@ -333,6 +338,7 @@ do_arc_correction(SegmentByView& out, const SegmentByView& in) con
assert(*in.get_proj_data_info_sptr() == *_noarc_corr_proj_data_info_sptr);
assert(*out.get_proj_data_info_sptr() == *_arc_corr_proj_data_info_sptr);
assert(out.get_segment_num() == in.get_segment_num());
+ assert(out.get_timing_pos_num() == in.get_timing_pos_num());
for (int view_num=in.get_min_view_num(); view_num<=in.get_max_view_num(); ++view_num)
for (int axial_pos_num=in.get_min_axial_pos_num(); axial_pos_num<=in.get_max_axial_pos_num(); ++axial_pos_num)
do_arc_correction(out[view_num][axial_pos_num], in[view_num][axial_pos_num]);
@@ -344,7 +350,7 @@ ArcCorrection::
do_arc_correction(const SegmentByView& in) const
{
SegmentByView out(_arc_corr_proj_data_info_sptr,
- in.get_segment_num());
+ in.get_segment_num(), in.get_timing_pos_num());
do_arc_correction(out, in);
return out;
}
@@ -359,16 +365,17 @@ do_arc_correction(ProjData& out, const ProjData& in) const
// Declare temporary viewgram out of the loop to avoid reallocation
// There is no default constructor, so we need to set it to some junk first.
Viewgram viewgram =
- _arc_corr_proj_data_info_sptr->get_empty_viewgram(in.get_min_view_num(), in.get_min_segment_num());
- for (int segment_num=in.get_min_segment_num(); segment_num<=in.get_max_segment_num(); ++segment_num)
- for (int view_num=in.get_min_view_num(); view_num<=in.get_max_view_num(); ++view_num)
- {
- viewgram =
- _arc_corr_proj_data_info_sptr->get_empty_viewgram(view_num, segment_num);
- do_arc_correction(viewgram, in.get_viewgram(view_num, segment_num));
- if (out.set_viewgram(viewgram) == Succeeded::no)
- return Succeeded::no;
- }
+ _arc_corr_proj_data_info_sptr->get_empty_viewgram(in.get_min_view_num(), in.get_min_segment_num(),in.get_min_tof_pos_num());
+ for (int timing_pos_num = in.get_min_tof_pos_num(); timing_pos_num <= in.get_max_tof_pos_num(); ++timing_pos_num)
+ for (int segment_num=in.get_min_segment_num(); segment_num<=in.get_max_segment_num(); ++segment_num)
+ for (int view_num=in.get_min_view_num(); view_num<=in.get_max_view_num(); ++view_num)
+ {
+ viewgram =
+ _arc_corr_proj_data_info_sptr->get_empty_viewgram(view_num, segment_num, false, timing_pos_num);
+ do_arc_correction(viewgram, in.get_viewgram(view_num, segment_num, false, timing_pos_num));
+ if (out.set_viewgram(viewgram) == Succeeded::no)
+ return Succeeded::no;
+ }
return Succeeded::yes;
}
diff --git a/src/buildblock/GeneralisedPoissonNoiseGenerator.cxx b/src/buildblock/GeneralisedPoissonNoiseGenerator.cxx
index 89c005967..9a38abc21 100644
--- a/src/buildblock/GeneralisedPoissonNoiseGenerator.cxx
+++ b/src/buildblock/GeneralisedPoissonNoiseGenerator.cxx
@@ -126,14 +126,19 @@ generate_random(ProjData& output_projdata,
for (int seg= input_projdata.get_min_segment_num();
seg<=input_projdata.get_max_segment_num();
seg++)
- {
- SegmentByView seg_output=
- output_projdata.get_empty_segment_by_view(seg);
-
- this->generate_random(seg_output, input_projdata.get_segment_by_view(seg));
- if (output_projdata.set_segment(seg_output) == Succeeded::no)
- error("Problem writing to projection data");
- }
+ {
+ for (int timing_pos_num = input_projdata.get_min_tof_pos_num();
+ timing_pos_num <= input_projdata.get_max_tof_pos_num();
+ ++timing_pos_num)
+ {
+ SegmentByView seg_output=
+ output_projdata.get_empty_segment_by_view(seg,false, timing_pos_num);
+
+ this->generate_random(seg_output, input_projdata.get_segment_by_view(seg, timing_pos_num));
+ if (output_projdata.set_segment(seg_output) == Succeeded::no)
+ error("Problem writing to projection data");
+ }
+ }
}
END_NAMESPACE_STIR
diff --git a/src/buildblock/KeyParser.cxx b/src/buildblock/KeyParser.cxx
index ab609755a..fb8b70705 100644
--- a/src/buildblock/KeyParser.cxx
+++ b/src/buildblock/KeyParser.cxx
@@ -2,7 +2,7 @@
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000 - 2009-04-30, Hammersmith Imanet Ltd
Copyright (C) 2011-07-01 - 2012-01-29, Kris Thielemans
- Copyright (C) 2020, 2021 University College London
+ Copyright (C) 2020, 2021, 2023, 2024 University College London
This file is part of STIR.
SPDX-License-Identifier: Apache-2.0 AND License-ref-PARAPET-license
@@ -30,20 +30,13 @@
#include "stir/error.h"
#include
#include
+#include
#include
#include
#include "stir/warning.h"
-# ifdef BOOST_NO_STDC_NAMESPACE
- namespace std { using ::getenv; }
-# endif
-
#include
-
-#ifndef BOOST_NO_STRINGSTREAM
#include
-#endif
-#ifndef STIR_NO_NAMESPACES
using std::ifstream;
using std::cerr;
using std::cout;
@@ -58,7 +51,6 @@ using std::list;
using std::pair;
using std::istream;
using std::ostream;
-#endif
START_NAMESPACE_STIR
@@ -276,6 +268,12 @@ bool KeyParser::parse(const char * const filename, const bool write_warning)
return parse(hdr_stream, write_warning);
}
+bool KeyParser::parse(const std::string& s, const bool write_warning)
+{
+ std::stringstream hdr_stream(s);
+ return parse(hdr_stream, write_warning);
+}
+
bool KeyParser::parse(istream& f, const bool write_warning)
{
// print_keywords_to_stream(cerr);
@@ -564,6 +562,15 @@ void KeyParser::add_key(const string& keyword,
add_in_keymap(keyword, map_element(t, &KeyParser::set_variable, variable, vectorised_key_level, list_of_values));
}
+void KeyParser::add_alias_key(const std::string& keyword, const std::string& alias, bool deprecated_key)
+{
+ const auto std_alias = standardise_keyword(alias);
+ const auto std_kw = standardise_keyword(keyword);
+ if (deprecated_key)
+ this->deprecated_alias_map[std_alias] = std_kw;
+ else
+ this->alias_map[std_alias] = std_kw;
+}
void
KeyParser::print_keywords_to_stream(ostream& out) const
@@ -623,6 +630,30 @@ Succeeded KeyParser::parse_header(const bool write_warning)
}
+std::string KeyParser::resolve_alias(const std::string& kw) const
+{
+ // search in alias_map
+ {
+ auto iter = alias_map.find(kw);
+ if (iter != alias_map.end())
+ {
+ return iter->second;
+ }
+ }
+ // search in deprecated_alias_map
+ {
+ auto iter = deprecated_alias_map.find(kw);
+ if (iter != deprecated_alias_map.end())
+ {
+ warning("KeyParser: found deprecated keyword '" + kw + "'. Replace with '" + iter->second
+ + "' to disable this warning and for future compatibility.");
+ return iter->second;
+ }
+ }
+ // not found: return original
+ return kw;
+}
+
Succeeded KeyParser::read_and_parse_line(const bool write_warning)
{
string line;
@@ -647,7 +678,7 @@ Succeeded KeyParser::read_and_parse_line(const bool write_warning)
}
// gets keyword
- keyword=standardise_keyword(get_keyword(line));
+ keyword=resolve_alias(standardise_keyword(get_keyword(line)));
return parse_value_in_line(line, write_warning);
}
diff --git a/src/buildblock/ML_norm.cxx b/src/buildblock/ML_norm.cxx
index 71944c31a..fd26331cb 100644
--- a/src/buildblock/ML_norm.cxx
+++ b/src/buildblock/ML_norm.cxx
@@ -1057,6 +1057,9 @@ static void make_fan_data_remove_gaps_help(FanProjData& fan_data,
const TProjDataInfo& proj_data_info,
const ProjData& proj_data)
{
+ if(proj_data.get_proj_data_info_sptr()->is_tof_data())
+ error("make_fan_data: Incompatible with TOF data. Abort.");
+
const int half_fan_size = fan_size/2;
const int num_virtual_axial_crystals_per_block =
proj_data_info.get_scanner_sptr()->
@@ -1148,6 +1151,10 @@ void make_fan_data_remove_gaps(FanProjData& fan_data,
int num_detectors_per_ring;
int fan_size;
int max_delta;
+
+ if(proj_data.get_proj_data_info_sptr()->is_tof_data())
+ error("make_fan_data: Incompatible with TOF data. Abort.");
+
const ProjDataInfo& proj_data_info =*proj_data.get_proj_data_info_sptr();
get_fan_info(num_rings, num_detectors_per_ring, max_delta, fan_size, proj_data_info);
@@ -1446,6 +1453,7 @@ void apply_efficiencies(FanProjData& fan_data, const DetectorEfficiencies& effic
}
}
+
void make_fan_sum_data(Array<2,float>& data_fan_sums, const FanProjData& fan_data)
{
for (int ra = fan_data.get_min_ra(); ra <= fan_data.get_max_ra(); ++ra)
@@ -1453,7 +1461,6 @@ void make_fan_sum_data(Array<2,float>& data_fan_sums, const FanProjData& fan_dat
data_fan_sums[ra][a] = fan_data.sum(ra,a);
}
-
template
static void make_fan_sum_data_help(Array<2,float>& data_fan_sums,
int num_rings, int num_detectors_per_ring,
@@ -1461,6 +1468,8 @@ static void make_fan_sum_data_help(Array<2,float>& data_fan_sums,
const TProjDataInfo& proj_data_info,
const ProjData& proj_data)
{
+ if(proj_data.get_proj_data_info_sptr()->is_tof_data())
+ error("make_fan_data: Incompatible with TOF data. Abort.");
const int half_fan_size = fan_size/2;
data_fan_sums.fill(0);
@@ -1470,26 +1479,29 @@ static void make_fan_sum_data_help(Array<2,float>& data_fan_sums,
for (bin.segment_num() = proj_data.get_min_segment_num(); bin.segment_num() <= proj_data.get_max_segment_num(); ++ bin.segment_num())
{
- segment_ptr.reset(new SegmentBySinogram(proj_data.get_segment_by_sinogram(bin.segment_num())));
+ // for (bin.timing_pos_num() = proj_data.get_min_tof_pos_num(); bin.timing_pos_num() <= proj_data.get_max_tof_pos_num(); ++ bin.timing_pos_num())
+ {
+ segment_ptr.reset(new SegmentBySinogram(proj_data.get_segment_by_sinogram(bin.segment_num()/*,bin.timing_pos_num()*/)));
- for (bin.axial_pos_num() = proj_data.get_min_axial_pos_num(bin.segment_num());
- bin.axial_pos_num() <= proj_data.get_max_axial_pos_num(bin.segment_num());
- ++bin.axial_pos_num())
- for (bin.view_num() = 0; bin.view_num() < num_detectors_per_ring/2; bin.view_num()++)
+ for (bin.axial_pos_num() = proj_data.get_min_axial_pos_num(bin.segment_num());
+ bin.axial_pos_num() <= proj_data.get_max_axial_pos_num(bin.segment_num());
+ ++bin.axial_pos_num())
+ for (bin.view_num() = 0; bin.view_num() < num_detectors_per_ring/2; bin.view_num()++)
for (bin.tangential_pos_num() = -half_fan_size;
- bin.tangential_pos_num() <= half_fan_size;
+ bin.tangential_pos_num() <= half_fan_size;
++bin.tangential_pos_num())
- {
- int ra = 0, a = 0;
- int rb = 0, b = 0;
+ {
+ int ra = 0, a = 0;
+ int rb = 0, b = 0;
- proj_data_info.get_det_pair_for_bin(a, ra, b, rb, bin);
+ proj_data_info.get_det_pair_for_bin(a, ra, b, rb, bin);
- const float value =
- (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()];
- data_fan_sums[ra][a] += value;
- data_fan_sums[rb][b] += value;
- }
+ const float value =
+ (*segment_ptr)[bin.axial_pos_num()][bin.view_num()][bin.tangential_pos_num()];
+ data_fan_sums[ra][a] += value;
+ data_fan_sums[rb][b] += value;
+ }
+ }
}
}
diff --git a/src/buildblock/ProjData.cxx b/src/buildblock/ProjData.cxx
index e4c86b5d0..c8582bab0 100644
--- a/src/buildblock/ProjData.cxx
+++ b/src/buildblock/ProjData.cxx
@@ -2,6 +2,7 @@
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000 - 2010-10-15, Hammersmith Imanet Ltd
Copyright (C) 2011-07-01 -2013, Kris Thielemans
+ Copyright (C) 2016, University of Hull
Copyright (C) 2015, 2020, 2022, 2023 University College London
Copyright (C) 2021-2022, Commonwealth Scientific and Industrial Research Organisation
Copyright (C) 2021, Rutherford Appleton Laboratory STFC
@@ -17,6 +18,7 @@
\brief Implementations for non-inline functions of class stir::ProjData
+ \author Nikos Efthimiou
\author Kris Thielemans
\author Ashley Gillman
\author Evgueni Ovtchinnikov
@@ -236,18 +238,18 @@ ProjData::get_subset(const std::vector& views) const
std::make_shared(proj_data_info_sptr, views);
unique_ptr subset_proj_data_uptr(new ProjDataInMemory(exam_info_sptr, subset_proj_data_info_sptr));
- //TODOTOF loop here
- for (int segment_num=get_min_segment_num(); segment_num<=get_max_segment_num(); ++segment_num)
- {
- for (int subset_view_num=0; subset_view_num < static_cast(views.size()); ++subset_view_num)
- {
- const Viewgram viewgram = this->get_viewgram(views[subset_view_num], segment_num);
- // construct new one with data from viewgram, but appropriate meta-data
- const Viewgram subset_viewgram(viewgram, subset_proj_data_info_sptr, subset_view_num, segment_num);
- if (subset_proj_data_uptr->set_viewgram(subset_viewgram) != Succeeded::yes)
- error("ProjData::get_subset failed to set a viewgram");
- }
- }
+ for (int timing_pos_num = this->get_min_tof_pos_num(); timing_pos_num <= this->get_max_tof_pos_num(); ++timing_pos_num)
+ for (int segment_num=get_min_segment_num(); segment_num<=get_max_segment_num(); ++segment_num)
+ {
+ for (int subset_view_num=0; subset_view_num < static_cast(views.size()); ++subset_view_num)
+ {
+ const auto viewgram = this->get_viewgram(views[subset_view_num], segment_num, false, timing_pos_num);
+ // construct new one with data from viewgram, but appropriate meta-data
+ const Viewgram subset_viewgram(viewgram, subset_proj_data_info_sptr, subset_view_num, segment_num, timing_pos_num);
+ if (subset_proj_data_uptr->set_viewgram(subset_viewgram) != Succeeded::yes)
+ error("ProjData::get_subset failed to set a viewgram");
+ }
+ }
return subset_proj_data_uptr;
}
@@ -267,37 +269,40 @@ ProjData::get_empty_sinogram(const SinogramIndices& ind) const
Viewgram
ProjData::get_empty_viewgram(const int view_num, const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos) const
{
return
- proj_data_info_sptr->get_empty_viewgram(view_num, segment_num, make_num_tangential_poss_odd);
+ proj_data_info_sptr->get_empty_viewgram(view_num, segment_num, make_num_tangential_poss_odd, timing_pos);
}
Sinogram
ProjData::get_empty_sinogram(const int ax_pos_num, const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos) const
{
return
- proj_data_info_sptr->get_empty_sinogram(ax_pos_num, segment_num, make_num_tangential_poss_odd);
+ proj_data_info_sptr->get_empty_sinogram(ax_pos_num, segment_num, make_num_tangential_poss_odd, timing_pos);
}
SegmentBySinogram
ProjData::get_empty_segment_by_sinogram(const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos) const
{
return
- proj_data_info_sptr->get_empty_segment_by_sinogram(segment_num, make_num_tangential_poss_odd);
+ proj_data_info_sptr->get_empty_segment_by_sinogram(segment_num, make_num_tangential_poss_odd, timing_pos);
}
SegmentByView
ProjData::get_empty_segment_by_view(const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos) const
{
return
- proj_data_info_sptr->get_empty_segment_by_view(segment_num, make_num_tangential_poss_odd);
-
+ proj_data_info_sptr->get_empty_segment_by_view(segment_num, make_num_tangential_poss_odd, timing_pos);
}
SegmentBySinogram
@@ -314,18 +319,21 @@ ProjData::get_empty_segment_by_view(const SegmentIndices& ind) const
RelatedViewgrams
ProjData::get_empty_related_viewgrams(const ViewgramIndices& view_segmnet_num,
- const shared_ptr& symmetries_used,
- const bool make_num_tangential_poss_odd) const
+ const shared_ptr& symmetries_used,
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos) const
+
{
return
- proj_data_info_sptr->get_empty_related_viewgrams(view_segmnet_num, symmetries_used, make_num_tangential_poss_odd);
+ proj_data_info_sptr->get_empty_related_viewgrams(view_segmnet_num, symmetries_used, make_num_tangential_poss_odd, timing_pos);
}
RelatedViewgrams
ProjData::get_related_viewgrams(const ViewgramIndices& viewgram_indices,
- const shared_ptr& symmetries_used,
- const bool make_num_bins_odd) const
+ const shared_ptr& symmetries_used,
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos) const
{
vector pairs;
symmetries_used->get_related_view_segment_numbers(
@@ -339,13 +347,30 @@ ProjData::get_related_viewgrams(const ViewgramIndices& viewgram_indices,
for (unsigned int i=0; iget_viewgram(pairs[i]));
}
return RelatedViewgrams(viewgrams, symmetries_used);
}
+//std::vector
+//ProjData::get_related_bin_values(const std::vector& r_bins) const
+//{
+
+// std::vector values;
+// values.reserve(r_bins.size());
+
+// for (std::vector ::const_iterator r_bins_iterator = r_bins.begin();
+// r_bins_iterator != r_bins.end(); ++r_bins_iterator)
+// {
+// values.push_back(this->get_bin_value(*r_bins_iterator));
+// }
+
+// return values;
+//}
+
Succeeded
ProjData::set_related_viewgrams( const RelatedViewgrams& viewgrams)
@@ -371,25 +396,23 @@ ProjData::set_related_viewgrams( const RelatedViewgrams& viewgrams)
}
#endif
-SegmentBySinogram ProjData::get_segment_by_sinogram(const int segment_num) const
+SegmentBySinogram ProjData::get_segment_by_sinogram(const int segment_num, const int timing_pos) const
{
SegmentBySinogram segment =
- proj_data_info_sptr->get_empty_segment_by_sinogram(segment_num,false);
+ proj_data_info_sptr->get_empty_segment_by_sinogram(segment_num,false,timing_pos);
// TODO optimise to get shared proj_data_info_ptr
for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num)
- segment.set_viewgram(get_viewgram(view_num, segment_num, false));
-
+ segment.set_viewgram(get_viewgram(view_num, segment_num, false, timing_pos));
return segment;
}
-SegmentByView ProjData::get_segment_by_view(const int segment_num) const
+SegmentByView ProjData::get_segment_by_view(const int segment_num, const int timing_pos) const
{
SegmentByView segment =
- proj_data_info_sptr->get_empty_segment_by_view(segment_num,false);
+ proj_data_info_sptr->get_empty_segment_by_view(segment_num,false,timing_pos);
// TODO optimise to get shared proj_data_info_ptr
for (int view_num = get_min_view_num(); view_num <= get_max_view_num(); ++view_num)
- segment.set_viewgram(get_viewgram(view_num, segment_num, false));
-
+ segment.set_viewgram(get_viewgram(view_num, segment_num, false, timing_pos));
return segment;
}
@@ -421,12 +444,15 @@ ProjData::set_segment(const SegmentByView& segment)
void
ProjData::fill(const float value)
{
- for (int segment_num = this->get_min_segment_num(); segment_num <= this->get_max_segment_num(); ++segment_num)
+ for (int timing_pos_num = this->get_min_tof_pos_num(); timing_pos_num <= this->get_max_tof_pos_num(); ++timing_pos_num)
{
- SegmentByView segment(this->get_empty_segment_by_view(segment_num));
- segment.fill(value);
- if(this->set_segment(segment) == Succeeded::no)
- error("Error setting segment of projection data");
+ for (int segment_num = this->get_min_segment_num(); segment_num <= this->get_max_segment_num(); ++segment_num)
+ {
+ SegmentByView segment(this->get_empty_segment_by_view(segment_num, false, timing_pos_num));
+ segment.fill(value);
+ if(this->set_segment(segment) == Succeeded::no)
+ error("Error setting segment of projection data");
+ }
}
}
@@ -441,9 +467,12 @@ ProjData::fill(const ProjData& proj_data)
for (int segment_num = this->get_min_segment_num(); segment_num <= this->get_max_segment_num(); ++segment_num)
{
- if(this->set_segment(proj_data.get_segment_by_view(segment_num))
- == Succeeded::no)
- error("Error setting segment of projection data");
+ for (int timing_pos_num = this->get_min_tof_pos_num(); timing_pos_num <= this->get_max_tof_pos_num(); ++timing_pos_num)
+ {
+ if(this->set_segment(proj_data.get_segment_by_view(segment_num, timing_pos_num))
+ == Succeeded::no)
+ error("Error setting segment of projection data");
+ }
}
}
@@ -464,18 +493,9 @@ write_to_file(const string& output_filename) const
ProjDataInterfile out_projdata(get_exam_info_sptr(),
this->proj_data_info_sptr, output_filename, ios::out);
- Succeeded success=Succeeded::yes;
- for (int segment_num = proj_data_info_sptr->get_min_segment_num();
- segment_num <= proj_data_info_sptr->get_max_segment_num();
- ++segment_num)
- {
- Succeeded success_this_segment =
- out_projdata.set_segment(get_segment_by_view(segment_num));
- if (success==Succeeded::yes)
- success = success_this_segment;
- }
- return success;
-
+ out_projdata.fill(*this);
+ // will have thrown if it failed, so return it was ok
+ return Succeeded::yes;
}
void
@@ -491,21 +511,23 @@ ProjData::
xapyb(const ProjData& x, const float a,
const ProjData& y, const float b)
{
- if (*get_proj_data_info_sptr() != *x.get_proj_data_info_sptr() ||
- *get_proj_data_info_sptr() != *y.get_proj_data_info_sptr())
- error("ProjData::xapyb: ProjDataInfo don't match");
+ if (*get_proj_data_info_sptr() != *x.get_proj_data_info_sptr() ||
+ *get_proj_data_info_sptr() != *y.get_proj_data_info_sptr())
+ error("ProjData::xapyb: ProjDataInfo don't match");
- const int n_min = get_min_segment_num();
- const int n_max = get_max_segment_num();
+ const int s_min = get_min_segment_num();
+ const int s_max = get_max_segment_num();
- for (int s=n_min; s<=n_max; ++s)
- {
- SegmentBySinogram seg = get_empty_segment_by_sinogram(s);
- const SegmentBySinogram sx = x.get_segment_by_sinogram(s);
- const SegmentBySinogram sy = y.get_segment_by_sinogram(s);
+ for (int timing_pos_num = this->get_min_tof_pos_num(); timing_pos_num <= this->get_max_tof_pos_num(); ++timing_pos_num)
+ for (int s=s_min; s<=s_max; ++s)
+ {
+ auto seg = get_empty_segment_by_sinogram(s, false, timing_pos_num);
+ const auto sx = x.get_segment_by_sinogram(s, timing_pos_num);
+ const auto sy = y.get_segment_by_sinogram(s, timing_pos_num);
seg.xapyb(sx, a, sy, b);
- set_segment(seg);
- }
+ if (set_segment(seg) == Succeeded::no)
+ error("ProjData::xapyb: set_segment failed. Write-only file?");
+ }
}
void
@@ -513,26 +535,28 @@ ProjData::
xapyb(const ProjData& x, const ProjData& a,
const ProjData& y, const ProjData& b)
{
- if (*get_proj_data_info_sptr() != *x.get_proj_data_info_sptr() ||
- *get_proj_data_info_sptr() != *y.get_proj_data_info_sptr() ||
- *get_proj_data_info_sptr() != *a.get_proj_data_info_sptr() ||
- *get_proj_data_info_sptr() != *b.get_proj_data_info_sptr())
- error("ProjData::xapyb: ProjDataInfo don't match");
+ if (*get_proj_data_info_sptr() != *x.get_proj_data_info_sptr() ||
+ *get_proj_data_info_sptr() != *y.get_proj_data_info_sptr() ||
+ *get_proj_data_info_sptr() != *a.get_proj_data_info_sptr() ||
+ *get_proj_data_info_sptr() != *b.get_proj_data_info_sptr())
+ error("ProjData::xapyb: ProjDataInfo don't match");
- const int n_min = get_min_segment_num();
- const int n_max = get_max_segment_num();
+ const int s_min = get_min_segment_num();
+ const int s_max = get_max_segment_num();
- for (int s=n_min; s<=n_max; ++s)
- {
- SegmentBySinogram seg = get_empty_segment_by_sinogram(s);
- const SegmentBySinogram sx = x.get_segment_by_sinogram(s);
- const SegmentBySinogram sy = y.get_segment_by_sinogram(s);
- const SegmentBySinogram sa = a.get_segment_by_sinogram(s);
- const SegmentBySinogram sb = b.get_segment_by_sinogram(s);
+ for (int timing_pos_num = this->get_min_tof_pos_num(); timing_pos_num <= this->get_max_tof_pos_num(); ++timing_pos_num)
+ for (int s=s_min; s<=s_max; ++s)
+ {
+ auto seg = get_empty_segment_by_sinogram(s, false, timing_pos_num);
+ const auto sx = x.get_segment_by_sinogram(s, timing_pos_num);
+ const auto sy = y.get_segment_by_sinogram(s, timing_pos_num);
+ const auto sa = a.get_segment_by_sinogram(s, timing_pos_num);
+ const auto sb = b.get_segment_by_sinogram(s, timing_pos_num);
seg.xapyb(sx, sa, sy, sb);
- set_segment(seg);
- }
+ if (set_segment(seg) == Succeeded::no)
+ error("ProjData::xapyb: set_segment failed. Write-only file?");
+ }
}
void
diff --git a/src/buildblock/ProjDataFromStream.cxx b/src/buildblock/ProjDataFromStream.cxx
index fbe677844..289ab9405 100644
--- a/src/buildblock/ProjDataFromStream.cxx
+++ b/src/buildblock/ProjDataFromStream.cxx
@@ -3,6 +3,7 @@
\ingroup projdata
\brief Implementations for non-inline functions of class stir::ProjDataFromStream
+ \author Nikos Efthimiou
\author Sanida Mustafovic
\author Kris Thielemans
\author Claire Labbe
@@ -12,7 +13,8 @@
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000 - 2011-12-21, Hammersmith Imanet Ltd
Copyright (C) 2011-2012, Kris Thielemans
- Copyright (C) 2013, University College London
+ Copyright (C) 2013, 2017, 2022, 2023 University College London
+ Copyright (C) 2016, University of Hull
This file is part of STIR.
@@ -66,16 +68,16 @@ START_NAMESPACE_STIR
//---------------------------------------------------------
ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_sptr,
- shared_ptr const& proj_data_info_ptr,
- shared_ptr const& s, const streamoff offs,
+ shared_ptr const& proj_data_info_sptr,
+ shared_ptr const& s, const streamoff offs,
const vector& segment_sequence_in_stream_v,
- StorageOrder o,
+ StorageOrder o,
NumericType data_type,
- ByteOrder byte_order,
+ ByteOrder byte_order,
float scale_factor)
-
+
:
- ProjData(exam_info_sptr, proj_data_info_ptr),
+ ProjData(exam_info_sptr, proj_data_info_sptr),
sino_stream(s), offset(offs),
segment_sequence(segment_sequence_in_stream_v),
storage_order(o),
@@ -85,17 +87,21 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_in
{
assert(storage_order != Unsupported);
assert(!(data_type == NumericType::UNKNOWN_TYPE));
+
+ if (proj_data_info_sptr->get_num_tof_poss() > 1)
+ activate_TOF();
+
}
ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_info_sptr,
- shared_ptr const& proj_data_info_ptr,
- shared_ptr const& s, const streamoff offs,
- StorageOrder o,
+ shared_ptr const& proj_data_info_sptr,
+ shared_ptr const& s, const streamoff offs,
+ StorageOrder o,
NumericType data_type,
- ByteOrder byte_order,
- float scale_factor)
+ ByteOrder byte_order,
+ float scale_factor)
:
- ProjData(exam_info_sptr, proj_data_info_ptr),
+ ProjData(exam_info_sptr, proj_data_info_sptr),
sino_stream(s), offset(offs),
storage_order(o),
on_disk_data_type(data_type),
@@ -105,20 +111,83 @@ ProjDataFromStream::ProjDataFromStream(shared_ptr const& exam_in
assert(storage_order != Unsupported);
assert(!(data_type == NumericType::UNKNOWN_TYPE));
- segment_sequence.resize(proj_data_info_ptr->get_num_segments());
+ segment_sequence.resize(proj_data_info_sptr->get_num_segments());
+ //N.E. Take this opportunity to calculate the size of the complete -full- 3D sinogram.
+ // We will need that to skip timing positions
int segment_num, i;
- for (i= 0, segment_num = proj_data_info_ptr->get_min_segment_num();
- segment_num<=proj_data_info_ptr->get_max_segment_num();
+
+ for (i= 0, segment_num = proj_data_info_sptr->get_min_segment_num();
+ segment_num<=proj_data_info_sptr->get_max_segment_num();
++i, ++segment_num)
{
segment_sequence[i] =segment_num;
}
+
+ if (proj_data_info_sptr->get_num_tof_poss() > 1)
+ activate_TOF();
+
+}
+
+void
+ProjDataFromStream::activate_TOF()
+{
+ int sum = 0;
+ for (int segment_num = proj_data_info_sptr->get_min_segment_num();
+ segment_num<=proj_data_info_sptr->get_max_segment_num();
+ ++segment_num)
+ {
+ sum += get_num_axial_poss(segment_num) * get_num_views() * get_num_tangential_poss();
+ }
+
+ offset_3d_data = static_cast (sum * on_disk_data_type.size_in_bytes());
+
+ // Now, lets initialise a TOF stream - Similarly to segments
+ if (storage_order == Segment_View_AxialPos_TangPos || storage_order == Timing_Segment_View_AxialPos_TangPos)
+ storage_order = Timing_Segment_View_AxialPos_TangPos;
+ else if (storage_order == Segment_AxialPos_View_TangPos || storage_order == Timing_Segment_AxialPos_View_TangPos)
+ storage_order = Timing_Segment_AxialPos_View_TangPos;
+ else
+ error("ProjDataFromStream: unsupported storage_order for TOF data");
+
+ timing_poss_sequence.resize(proj_data_info_sptr->get_num_tof_poss());
+
+ for (int i= 0, timing_pos_num = proj_data_info_sptr->get_min_tof_pos_num();
+ timing_pos_num<=proj_data_info_sptr->get_max_tof_pos_num();
+ ++i, ++timing_pos_num)
+ {
+ timing_poss_sequence[i] = timing_pos_num;
+ }
+
+}
+
+void
+ProjDataFromStream::set_timing_poss_sequence_in_stream(const std::vector& seq)
+{
+ this->timing_poss_sequence = seq;
+}
+
+namespace detail {
+ // 2 local functions to avoid cluttering code below
+
+ static void checked_seekg(const std::string& fname, std::istream& s, const std::streamoff offset)
+ {
+ s.seekg(offset, ios::beg);
+ if (!s)
+ error("ProjDataFromStream::" + fname + " error after seekg to offset " + std::to_string(offset));
+ }
+
+ static void checked_seekp(const std::string& fname, std::ostream& s, const std::streamoff offset)
+ {
+ s.seekp(offset, ios::beg);
+ if (!s)
+ error("ProjDataFromStream::" + fname + " error after seekp to offset " + std::to_string(offset));
+ }
}
-Viewgram
+Viewgram
ProjDataFromStream::get_viewgram(const int view_num, const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd, const int timing_pos) const
{
if (is_null_ptr(sino_stream))
{
@@ -128,70 +197,55 @@ ProjDataFromStream::get_viewgram(const int view_num, const int segment_num,
{
error("ProjDataFromStream::get_viewgram: error in stream state before reading\n");
}
-
- vector offsets = get_offsets(view_num,segment_num);
-
- const streamoff segment_offset = offsets[0];
- const streamoff beg_view_offset = offsets[1];
- const streamoff intra_views_offset = offsets[2];
-
- Viewgram viewgram(proj_data_info_sptr, view_num, segment_num);
+
+ Viewgram viewgram(proj_data_info_sptr, view_num, segment_num, timing_pos);
float scale = float(1);
- Succeeded succeeded = Succeeded::yes;
-
+ Succeeded succeeded = Succeeded::yes;
+ Bin bin(segment_num, view_num, this->get_min_axial_pos_num(segment_num), this->get_min_tangential_pos_num(), timing_pos);
+
#ifdef STIR_OPENMP
#pragma omp critical(PROJDATAFROMSTREAMIO)
#endif
- {
- sino_stream->seekg(segment_offset, ios::beg); // start of segment
- sino_stream->seekg(beg_view_offset, ios::cur); // start of view within segment
-
- if (! *sino_stream)
- {
- warning("ProjDataFromStream::get_viewgram: error after seekg");
- succeeded = Succeeded::no;
- }
- else if (get_storage_order() == Segment_AxialPos_View_TangPos)
- {
- for (int ax_pos_num = get_min_axial_pos_num(segment_num); ax_pos_num <= get_max_axial_pos_num(segment_num); ax_pos_num++)
- {
- if (read_data(*sino_stream, viewgram[ax_pos_num], on_disk_data_type, scale, on_disk_byte_order)
- == Succeeded::no)
- {
- succeeded = Succeeded::no;
+ try
+ {
+ if (get_storage_order() == Segment_AxialPos_View_TangPos || get_storage_order() == Timing_Segment_AxialPos_View_TangPos)
+ {
+ for (bin.axial_pos_num() = get_min_axial_pos_num(segment_num); bin.axial_pos_num() <= get_max_axial_pos_num(segment_num); bin.axial_pos_num()++)
+ {
+ detail::checked_seekg("get_viewgram", *sino_stream, get_offset(bin));
+ if ((succeeded = read_data(*sino_stream, viewgram[bin.axial_pos_num()], on_disk_data_type, scale, on_disk_byte_order))
+ == Succeeded::no)
break;
- }
- else if(scale != 1)
- {
- warning("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1");
- succeeded = Succeeded::no;
+ if(scale != 1)
break;
- }
- // seek to next line unless it was the last we need to read
- if(ax_pos_num != get_max_axial_pos_num(segment_num))
- sino_stream->seekg(intra_views_offset, ios::cur);
- }
- }
- else if (get_storage_order() == Segment_View_AxialPos_TangPos)
- {
- if(read_data(*sino_stream, viewgram, on_disk_data_type, scale, on_disk_byte_order)
- == Succeeded::no)
- {
- succeeded = Succeeded::no;
- }
- else if(scale != 1)
- {
- warning("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1");
- succeeded = Succeeded::no;
- }
- }
- } // end of critical section
+ }
+ }
+ else if (get_storage_order() == Segment_View_AxialPos_TangPos || get_storage_order() == Timing_Segment_View_AxialPos_TangPos)
+ {
+ // read in one go (skipping the extra seek)
+ detail::checked_seekg("get_viewgram", *sino_stream, get_offset(bin));
+ succeeded = read_data(*sino_stream, viewgram, on_disk_data_type, scale, on_disk_byte_order);
+ }
+ else
+ {
+ warning("ProjDataFromStream::get_viewgram: unsupported storage order");
+ succeeded = Succeeded::no;
+ }
+ }
+ catch (...)
+ {
+ succeeded = Succeeded::no;
+ }
+ // end of critical section
+ if(scale != 1)
+ error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1");
if (succeeded == Succeeded::no)
- error("ProjDataFromStream: error reading data");
+ error("ProjDataFromStream: error reading data (file truncated?)");
viewgram *= scale_factor;
if (make_num_tangential_poss_odd &&(get_num_tangential_poss()%2==0))
+
{
const int new_max_tangential_pos = get_max_tangential_pos_num() + 1;
@@ -217,22 +271,11 @@ ProjDataFromStream::get_bin_value(const Bin& this_bin) const
error("ProjDataFromStream::get_bin_value: error in stream state before reading\n");
}
- vector offsets = get_offsets_bin(this_bin);
-
- const streamoff total_offset = offsets[0];
-
- sino_stream->seekg(0 , ios::beg); // reset file
- sino_stream->seekg(total_offset, ios::cur); // start of view within segment
-
- if (! *sino_stream)
- {
- error("ProjDataFromStream::get_bin_value: error after seekg.");
- }
+ detail::checked_seekg("get_bin_value", *sino_stream, get_offset(this_bin));
Array< 1, float> value(1);
float scale = float(1);
- // Now the storage order is not more important. Just read.
if (read_data(*sino_stream, value, on_disk_data_type, scale, on_disk_byte_order)
== Succeeded::no)
error("ProjDataFromStream: error reading data\n");
@@ -256,17 +299,7 @@ ProjDataFromStream::set_bin_value(const Bin& this_bin)
error("ProjDataFromStream::set_bin_value: error in stream state before writing");
}
- vector offsets = get_offsets_bin(this_bin);
-
- const streamoff total_offset = offsets[0];
-
- sino_stream->seekp(0 , ios::beg); // reset file
- sino_stream->seekp(total_offset, ios::cur); // start of view within segment
-
- if (! *sino_stream)
- {
- error("ProjDataFromStream::set_bin_value: error after seekp.");
- }
+ detail::checked_seekp("set_bin_value", *sino_stream, get_offset(this_bin));
Array< 1, float> value(1);
value[0]=this_bin.get_bin_value();
@@ -279,76 +312,6 @@ ProjDataFromStream::set_bin_value(const Bin& this_bin)
error("ProjDataFromStream: error writing data: scale factor returned by write_data should be 1\n");
}
-vector
-ProjDataFromStream::get_offsets(const int view_num, const int segment_num) const
-
-{
- if (!(segment_num >= get_min_segment_num() &&
- segment_num <= get_max_segment_num()))
- error("ProjDataFromStream::get_offsets: segment_num out of range : %d", segment_num);
-
- if (!(view_num >= get_min_view_num() &&
- view_num <= get_max_view_num()))
- error("ProjDataFromStream::get_offsets: view_num out of range : %d", view_num);
-
-
- // cout<<"get_offsets"<(FIND(segment_sequence.begin(), segment_sequence.end(), segment_num) -
- segment_sequence.begin());
-
- streamoff num_axial_pos_offset = 0;
- for (int i=0; i(num_axial_pos_offset*
- get_num_tangential_poss() *
- get_num_views() *
- on_disk_data_type.size_in_bytes());
-
- if (get_storage_order() == Segment_AxialPos_View_TangPos)
- {
-
-
- const streamoff beg_view_offset =
- (view_num - get_min_view_num()) *get_num_tangential_poss() * on_disk_data_type.size_in_bytes();
-
- const streamoff intra_views_offset =
- (get_num_views() -1) *get_num_tangential_poss() * on_disk_data_type.size_in_bytes();
- vector temp(3);
- temp[0] = segment_offset;
- temp[1] = beg_view_offset;
- temp[2] = intra_views_offset;
-
- return temp;
- }
- else //if (get_storage_order() == Segment_View_AxialPos_TangPos)
- {
- const streamoff beg_view_offset =
- (view_num - get_min_view_num())
- * get_num_axial_poss(segment_num)
- * get_num_tangential_poss()
- * on_disk_data_type.size_in_bytes();
-
-
- vector temp(3);
- temp[0] =segment_offset;
- temp[1]= beg_view_offset;
- temp[2] = 0;
- return temp;
-
- }
-}
-
Succeeded
ProjDataFromStream::set_viewgram(const Viewgram& v)
{
@@ -368,23 +331,22 @@ ProjDataFromStream::set_viewgram(const Viewgram& v)
{
warning("ProjDataFromStream::set_viewgram: non-float output uses original "
"scale factor %g which might not be appropriate for the current data\n",
- scale_factor);
+ scale_factor);
}
if (get_num_tangential_poss() != v.get_proj_data_info_sptr()->get_num_tangential_poss())
{
- warning("ProjDataFromStream::set_viewgram: num_bins is not correct\n");
+ warning("ProjDataFromStream::set_viewgram: num_bins is not correct\n");
return Succeeded::no;
}
if (get_num_axial_poss(v.get_segment_num()) != v.get_num_axial_poss())
{
- warning("ProjDataFromStream::set_viewgram: number of axial positions is not correct\n");
+ warning("ProjDataFromStream::set_viewgram: number of axial positions is not correct\n");
return Succeeded::no;
}
-
if (*get_proj_data_info_sptr() != *(v.get_proj_data_info_sptr()))
{
warning("ProjDataFromStream::set_viewgram: viewgram has incompatible ProjDataInfo member\n"
@@ -396,89 +358,79 @@ ProjDataFromStream::set_viewgram(const Viewgram& v)
return Succeeded::no;
}
- int segment_num = v.get_segment_num();
- int view_num = v.get_view_num();
-
-
- vector offsets = get_offsets(view_num,segment_num);
- const streamoff segment_offset = offsets[0];
- const streamoff beg_view_offset = offsets[1];
- const streamoff intra_views_offset = offsets[2];
+ const int segment_num = v.get_segment_num();
+ const int view_num = v.get_view_num();
+ const int timing_pos = v.get_timing_pos_num();
+ Bin bin(segment_num, view_num, this->get_min_axial_pos_num(segment_num), this->get_min_tangential_pos_num(), timing_pos);
+
float scale = scale_factor;
Succeeded succeeded = Succeeded::yes;
#ifdef STIR_OPENMP
#pragma omp critical(PROJDATAFROMSTREAMIO)
#endif
- {
- sino_stream->seekp(segment_offset, ios::beg); // start of segment
- sino_stream->seekp(beg_view_offset, ios::cur); // start of view within segment
-
- if (! *sino_stream)
- {
- warning("ProjDataFromStream::set_viewgram: error after seekp");
- succeeded = Succeeded::no;
- }
- else if (get_storage_order() == Segment_AxialPos_View_TangPos)
- {
- for (int ax_pos_num = get_min_axial_pos_num(segment_num); ax_pos_num <= get_max_axial_pos_num(segment_num); ax_pos_num++)
- {
-
- if (write_data(*sino_stream, v[ax_pos_num], on_disk_data_type, scale, on_disk_byte_order)
- == Succeeded::no
- || scale != scale_factor)
- {
- warning("ProjDataFromStream::set_viewgram: viewgram (view=%d, segment=%d)"
- " corrupted due to problems with writing or the scale factor \n",
- view_num, segment_num);
- succeeded = Succeeded::no;
- break;
- }
- // seek to next line unless it was the last we need to read
- if(ax_pos_num != get_max_axial_pos_num(segment_num))
- sino_stream->seekp(intra_views_offset, ios::cur);
- }
+ try
+ {
+ if (get_storage_order() == Segment_AxialPos_View_TangPos || get_storage_order() == Timing_Segment_AxialPos_View_TangPos)
+ {
+ for (bin.axial_pos_num() = get_min_axial_pos_num(segment_num); bin.axial_pos_num() <= get_max_axial_pos_num(segment_num); bin.axial_pos_num()++)
+ {
+ detail::checked_seekp("set_viewgram", *sino_stream, get_offset(bin));
+ if (write_data(*sino_stream, v[bin.axial_pos_num()], on_disk_data_type, scale, on_disk_byte_order)
+ == Succeeded::no
+ || scale != scale_factor)
+ {
+ succeeded = Succeeded::no;
+ break;
+ }
+ }
+ }
+ else if (get_storage_order() == Segment_View_AxialPos_TangPos || get_storage_order() == Timing_Segment_View_AxialPos_TangPos)
+ {
+ // write in one go (skipping the extra seek)
+ detail::checked_seekp( "set_viewgram", *sino_stream, get_offset(bin));
+ if (write_data(*sino_stream, v, on_disk_data_type, scale, on_disk_byte_order) == Succeeded::no
+ || scale != scale_factor)
+ {
+ succeeded = Succeeded::no;
+ }
+ }
+ else
+ {
+ warning("ProjDataFromStream::set_viewgram: unsupported storage order");
+ succeeded = Succeeded::no;
+ }
+ // flush the stream, see the class documentation
+ sino_stream->flush();
+ }
+ catch (...)
+ {
+ succeeded = Succeeded::no;
+ }
+ // end of critical section
+ if (succeeded == Succeeded::no)
+ error("ProjDataFromStream::set_viewgram: viewgram (view=%d, segment=%d, timing_pos=%d)"
+ " corrupted due to problems with writing or the scale factor (out of disk space?)",
+ view_num, segment_num, timing_pos);
- // flush the stream, see the class documentation
- sino_stream->flush();
- }
- else if (get_storage_order() == Segment_View_AxialPos_TangPos)
- {
- if (write_data(*sino_stream, v, on_disk_data_type, scale, on_disk_byte_order)
- == Succeeded::no
- || scale != scale_factor)
- {
- warning("ProjDataFromStream::set_viewgram: viewgram (view=%d, segment=%d)"
- " corrupted due to problems with writing or the scale factor \n",
- view_num, segment_num);
- succeeded = Succeeded::no;
- }
- // flush the stream, see the class documentation
- sino_stream->flush();
- succeeded = Succeeded::yes;
- }
- else
- {
- warning("ProjDataFromStream::set_viewgram: unsupported storage order\n");
- succeeded = Succeeded::no;
- }
- } // end of critical section
return succeeded;
}
-std::vector
-ProjDataFromStream::get_offsets_bin(const Bin this_bin) const
+std::streamoff
+ProjDataFromStream::get_offset(const Bin& this_bin) const
{
if (!(this_bin.segment_num() >= get_min_segment_num() &&
this_bin.segment_num() <= get_max_segment_num()))
- error("ProjDataFromStream::get_offsets: segment_num out of range : %d", this_bin.segment_num());
+ error("ProjDataFromStream::get_offset: segment_num out of range : %d", this_bin.segment_num());
if (!(this_bin.axial_pos_num() >= get_min_axial_pos_num(this_bin.segment_num()) &&
this_bin.axial_pos_num() <= get_max_axial_pos_num(this_bin.segment_num())))
- error("ProjDataFromStream::get_offsets: axial_pos_num out of range : %d", this_bin.axial_pos_num());
-
+ error("ProjDataFromStream::get_offset: axial_pos_num out of range : %d", this_bin.axial_pos_num());
+ if (!(this_bin.timing_pos_num() >= get_min_tof_pos_num() &&
+ this_bin.timing_pos_num() <= get_max_tof_pos_num()))
+ error("ProjDataFromStream::get_offset: timing_num out of range : %d", this_bin.timing_pos_num());
const int index =
static_cast(FIND(segment_sequence.begin(), segment_sequence.end(), this_bin.segment_num()) -
@@ -490,7 +442,7 @@ ProjDataFromStream::get_offsets_bin(const Bin this_bin) const
num_axial_pos_offset +=
get_num_axial_poss(segment_sequence[i]);
- const streamoff segment_offset =
+ streamoff segment_offset =
offset +
static_cast(num_axial_pos_offset*
get_num_tangential_poss() *
@@ -498,8 +450,17 @@ ProjDataFromStream::get_offsets_bin(const Bin this_bin) const
on_disk_data_type.size_in_bytes());
// Now we are just in front of the correct segment
- if (get_storage_order() == Segment_AxialPos_View_TangPos)
+ if (get_storage_order() == Segment_AxialPos_View_TangPos || get_storage_order() == Timing_Segment_AxialPos_View_TangPos)
{
+ if (proj_data_info_sptr->get_num_tof_poss() > 1)
+ {
+ // The timing offset will be added to the segment offset to minimise the changes
+ const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), this_bin.timing_pos_num()) -
+ timing_poss_sequence.begin());
+
+ assert(offset_3d_data > 0);
+ segment_offset += static_cast(timing_index) * offset_3d_data;
+ }
// skip axial positions
const streamoff ax_pos_offset =
(this_bin.axial_pos_num() - get_min_axial_pos_num(this_bin.segment_num()))*
@@ -519,14 +480,19 @@ ProjDataFromStream::get_offsets_bin(const Bin this_bin) const
const streamoff tang_offset =
(this_bin.tangential_pos_num() - get_min_tangential_pos_num()) * on_disk_data_type.size_in_bytes();
- vector temp(1);
- temp[0] = segment_offset + ax_pos_offset + view_offset +tang_offset;
-
- return temp;
+ return segment_offset + ax_pos_offset + view_offset +tang_offset;
}
- else //if (get_storage_order() == Segment_View_AxialPos_TangPos)
+ else if (get_storage_order() == Segment_View_AxialPos_TangPos || get_storage_order() == Timing_Segment_View_AxialPos_TangPos)
{
+ if (proj_data_info_sptr->get_num_tof_poss() > 1)
+ {
+ // The timing offset will be added to the segment offset. This approach we minimise the changes
+ const int timing_index = static_cast(FIND(timing_poss_sequence.begin(), timing_poss_sequence.end(), this_bin.timing_pos_num()) -
+ timing_poss_sequence.begin());
+ assert(offset_3d_data > 0);
+ segment_offset += static_cast(timing_index) * offset_3d_data;
+ }
// Skip views
const streamoff view_offset =
(this_bin.view_num() - get_min_view_num())*
@@ -545,161 +511,71 @@ ProjDataFromStream::get_offsets_bin(const Bin this_bin) const
const streamoff tang_offset =
(this_bin.tangential_pos_num() - get_min_tangential_pos_num()) * on_disk_data_type.size_in_bytes();
- vector temp(1);
- temp[0] = segment_offset + ax_pos_offset +view_offset + tang_offset;
-
- return temp;
+ return segment_offset + ax_pos_offset +view_offset + tang_offset;
+ }
+ else
+ {
+ error("ProjDataFromStream::get_offset: unsupported storage order");
+ return streamoff(0); // return something to avoid compiler warning
}
-}
-
-// get offsets for the sino data
-vector
-ProjDataFromStream::get_offsets_sino(const int ax_pos_num, const int segment_num) const
-{
- if (!(segment_num >= get_min_segment_num() &&
- segment_num <= get_max_segment_num()))
- error("ProjDataFromStream::get_offsets: segment_num out of range : %d", segment_num);
-
- if (!(ax_pos_num >= get_min_axial_pos_num(segment_num) &&
- ax_pos_num <= get_max_axial_pos_num(segment_num)))
- error("ProjDataFromStream::get_offsets: axial_pos_num out of range : %d", ax_pos_num);
-
- const int index =
- static_cast(FIND(segment_sequence.begin(), segment_sequence.end(), segment_num) -
- segment_sequence.begin());
-
-
- streamoff num_axial_pos_offset = 0;
- for (int i=0; i(num_axial_pos_offset*
- get_num_tangential_poss() *
- get_num_views() *
- on_disk_data_type.size_in_bytes());
-
- if (get_storage_order() == Segment_AxialPos_View_TangPos)
- {
-
- const streamoff beg_ax_pos_offset =
- (ax_pos_num - get_min_axial_pos_num(segment_num))*
- get_num_views() *
- get_num_tangential_poss()*
- on_disk_data_type.size_in_bytes();
-
- vector temp(3);
- temp[0] = segment_offset;
- temp[1] = beg_ax_pos_offset;
- temp[2] = 0;
-
- return temp;
- }
- else //if (get_storage_order() == Segment_View_AxialPos_TangPos)
- {
-
-
- const streamoff beg_ax_pos_offset =
- (ax_pos_num - get_min_axial_pos_num(segment_num)) *get_num_tangential_poss() * on_disk_data_type.size_in_bytes();
-
- const streamoff intra_ax_pos_offset =
- (get_num_axial_poss(segment_num) -1) *get_num_tangential_poss() * on_disk_data_type.size_in_bytes();
-
-
- vector temp(3);
- temp[0] =segment_offset;
- temp[1]= beg_ax_pos_offset;
- temp[2] =intra_ax_pos_offset;
- return temp;
-
- }
}
Sinogram
ProjDataFromStream::get_sinogram(const int ax_pos_num, const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd, const int timing_pos) const
{
if (is_null_ptr(sino_stream))
{
- error("ProjDataFromStream::get_sinogram: stream ptr is 0\n");
+ error("ProjDataFromStream::get_sinogram: stream ptr is 0");
}
if (! *sino_stream)
{
- error("ProjDataFromStream::get_sinogram: error in stream state before reading\n");
+ error("ProjDataFromStream::get_sinogram: error in stream state before reading");
}
-
- // Call the get_offset to calculate the offsets, e.g
- // segment offset + view_offset + intra_view_offsets
- vector offsets = get_offsets_sino(ax_pos_num,segment_num);
-
- const streamoff segment_offset = offsets[0];
- const streamoff beg_ax_pos_offset = offsets[1];
- const streamoff intra_ax_pos_offset = offsets[2];
- Sinogram sinogram(proj_data_info_sptr, ax_pos_num, segment_num);
+
+ Sinogram sinogram(proj_data_info_sptr, ax_pos_num, segment_num, timing_pos);
float scale = float(1);
Succeeded succeeded = Succeeded::yes;
+ Bin bin(segment_num, this->get_min_view_num(), ax_pos_num, this->get_min_tangential_pos_num(), timing_pos);
- if (get_storage_order() == Segment_AxialPos_View_TangPos)
- {
-#ifdef STIR_OPENMP
-#pragma omp critical(PROJDATAFROMSTREAMIO)
-#endif
- {
- sino_stream->seekg(segment_offset, ios::beg); // start of segment
- sino_stream->seekg(beg_ax_pos_offset, ios::cur); // start of view within segment
- if (! *sino_stream)
- {
- warning("ProjDataFromStream::get_sinogram: error after seekg");
- succeeded = Succeeded::no;
- }
- else
- {
- succeeded = read_data(*sino_stream, sinogram, on_disk_data_type, scale, on_disk_byte_order);
- }
- } // end of critical section
- if (succeeded == Succeeded::no)
- error("ProjDataFromStream: error reading data");
- if(scale != 1)
- error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1");
- }
- else if (get_storage_order() == Segment_View_AxialPos_TangPos)
- {
#ifdef STIR_OPENMP
#pragma omp critical(PROJDATAFROMSTREAMIO)
#endif
+ try
+ {
+ if (get_storage_order() == Segment_AxialPos_View_TangPos || get_storage_order() == Timing_Segment_AxialPos_View_TangPos)
{
- sino_stream->seekg(segment_offset, ios::beg); // start of segment
- sino_stream->seekg(beg_ax_pos_offset, ios::cur); // start of view within segment
- if (! *sino_stream)
- {
- warning("ProjDataFromStream::get_sinogram: error after seekg");
- succeeded = Succeeded::no;
- }
- for (int view = get_min_view_num(); view <= get_max_view_num(); view++)
+ detail::checked_seekg("get_sinogram", *sino_stream, get_offset(bin));
+ succeeded = read_data(*sino_stream, sinogram, on_disk_data_type, scale, on_disk_byte_order);
+ }
+ else if (get_storage_order() == Segment_View_AxialPos_TangPos || get_storage_order() == Timing_Segment_View_AxialPos_TangPos)
+ {
+ for (bin.view_num() = get_min_view_num(); bin.view_num() <= get_max_view_num(); bin.view_num()++)
{
- if (read_data(*sino_stream, sinogram[view], on_disk_data_type, scale, on_disk_byte_order)
- == Succeeded::no)
- {
- succeeded = Succeeded::no;
- break;
- }
- else if(scale != 1)
- {
- warning("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1");
- succeeded = Succeeded::no;
- break;
- }
- // seek to next line unless it was the last we need to read
- if(view != get_max_view_num())
- sino_stream->seekg(intra_ax_pos_offset, ios::cur);
+ detail::checked_seekg("get_sinogram", *sino_stream, get_offset(bin));
+ if ((succeeded = read_data(*sino_stream, sinogram[bin.view_num()], on_disk_data_type, scale, on_disk_byte_order))
+ == Succeeded::no)
+ break;
+ if(scale != 1)
+ break;
}
- } // end of critical section
- }
+ }
+ else
+ {
+ warning("ProjDataFromStream::get_sinogram: unsupported storage order");
+ succeeded = Succeeded::no;
+ }
+ }
+ catch (...)
+ {
+ succeeded = Succeeded::no;
+ }
+ // end of critical section
+ if(scale != 1)
+ error("ProjDataFromStream: error reading data: scale factor returned by read_data should be 1");
if (succeeded == Succeeded::no)
error("ProjDataFromStream: error reading data");
+
sinogram *= scale_factor;
if (make_num_tangential_poss_odd&&(get_num_tangential_poss()%2==0))
@@ -733,9 +609,9 @@ ProjDataFromStream::set_sinogram(const Sinogram& s)
{
warning("ProjDataFromStream::set_sinogram: non-float output uses original "
"scale factor %g which might not be appropriate for the current data\n",
- scale_factor);
+ scale_factor);
}
-
+
if (*get_proj_data_info_sptr() != *(s.get_proj_data_info_sptr()))
{
warning("ProjDataFromStream::set_sinogram: Sinogram has incompatible ProjDataInfo member.\n"
@@ -747,108 +623,66 @@ ProjDataFromStream::set_sinogram(const Sinogram& s)
return Succeeded::no;
}
- int segment_num = s.get_segment_num();
+ int segment_num = s.get_segment_num();
int ax_pos_num = s.get_axial_pos_num();
-
-
- vector offsets = get_offsets_sino(ax_pos_num,segment_num);
- const streamoff segment_offset = offsets[0];
- const streamoff beg_ax_pos_offset = offsets[1];
- const streamoff intra_ax_pos_offset = offsets[2];
+ int timing_pos = s.get_timing_pos_num();
+ Bin bin(segment_num, this->get_min_view_num(), ax_pos_num, this->get_min_tangential_pos_num(), timing_pos);
float scale = scale_factor;
Succeeded succeeded = Succeeded::yes;
#ifdef STIR_OPENMP
#pragma omp critical(PROJDATAFROMSTREAMIO)
#endif
- {
- sino_stream->seekp(segment_offset, ios::beg); // start of segment
- sino_stream->seekp(beg_ax_pos_offset, ios::cur); // start of view within segment
-
- if (! *sino_stream)
- {
- warning("ProjDataFromStream::set_sinogram: error after seekg\n");
- succeeded = Succeeded::no;
- }
-
- if (get_storage_order() == Segment_AxialPos_View_TangPos)
-
- {
- if (write_data(*sino_stream, s, on_disk_data_type, scale, on_disk_byte_order)
- == Succeeded::no
- || scale != scale_factor)
- {
- warning("ProjDataFromStream::set_sinogram: sinogram (ax_pos=%d, segment=%d)"
- " corrupted due to problems with writing or the scale factor \n",
- ax_pos_num, segment_num);
- succeeded = Succeeded::no;
- }
- // flush the stream, see the class documentation
- sino_stream->flush();
- }
-
- else if (get_storage_order() == Segment_View_AxialPos_TangPos)
- {
- for (int view = get_min_view_num();view <= get_max_view_num(); view++)
- {
- if (write_data(*sino_stream, s[view], on_disk_data_type, scale, on_disk_byte_order)
- == Succeeded::no
- || scale != scale_factor)
- {
- warning("ProjDataFromStream::set_sinogram: sinogram (ax_pos=%d, segment=%d)"
- " corrupted due to problems with writing or the scale factor \n",
- ax_pos_num, segment_num);
- succeeded = Succeeded::no;
- break;
- }
- // seek to next line unless it was the last we need to read
- if(view != get_max_view_num())
- sino_stream->seekp(intra_ax_pos_offset, ios::cur);
- }
- // flush the stream, see the class documentation
- sino_stream->flush();
- }
- else
- {
- warning("ProjDataFromStream::set_sinogram: unsupported storage order");
- succeeded = Succeeded::no;
- }
- } // end of critical section
+ try
+ {
+ if (get_storage_order() == Segment_AxialPos_View_TangPos || get_storage_order() == Timing_Segment_AxialPos_View_TangPos)
+ {
+ detail::checked_seekp("set_sinogram", *sino_stream, get_offset(bin));
+ if (write_data(*sino_stream, s, on_disk_data_type, scale, on_disk_byte_order)
+ == Succeeded::no
+ || scale != scale_factor)
+ {
+ warning("ProjDataFromStream::set_sinogram: sinogram (ax_pos=%d, segment=%d)"
+ " corrupted due to problems with writing or the scale factor \n",
+ ax_pos_num, segment_num);
+ succeeded = Succeeded::no;
+ }
+ }
+ else if (get_storage_order() == Segment_View_AxialPos_TangPos || get_storage_order() == Timing_Segment_View_AxialPos_TangPos)
+ {
+ for (bin.view_num() = get_min_view_num(); bin.view_num() <= get_max_view_num(); bin.view_num()++)
+ {
+ detail::checked_seekp("set_sinogram", *sino_stream, get_offset(bin));
+ if (write_data(*sino_stream, s[bin.view_num()], on_disk_data_type, scale, on_disk_byte_order)
+ == Succeeded::no
+ || scale != scale_factor)
+ {
+ warning("ProjDataFromStream::set_sinogram: sinogram (ax_pos=%d, segment=%d)"
+ " corrupted due to problems with writing or the scale factor \n",
+ ax_pos_num, segment_num);
+ succeeded = Succeeded::no;
+ break;
+ }
+ }
+ }
+ else
+ {
+ warning("ProjDataFromStream::set_sinogram: unsupported storage order");
+ succeeded = Succeeded::no;
+ }
+ // flush the stream, see the class documentation
+ sino_stream->flush();
+ }
+ catch (...)
+ {
+ succeeded = Succeeded::no;
+ }
+ // end of critical section
return succeeded;
}
-streamoff
-ProjDataFromStream::get_offset_segment(const int segment_num) const
-{
- assert(segment_num >= get_min_segment_num() &&
- segment_num <= get_max_segment_num());
- {
- const int index =
- static_cast(FIND(segment_sequence.begin(), segment_sequence.end(), segment_num) -
- segment_sequence.begin());
-
- streamoff num_axial_pos_offset = 0;
- for (int i=0; i No need for get_offset_segment
-
SegmentBySinogram
-ProjDataFromStream::get_segment_by_sinogram(const int segment_num) const
+ProjDataFromStream::get_segment_by_sinogram(const int segment_num, const int timing_num) const
{
if(is_null_ptr(sino_stream))
{
@@ -858,28 +692,26 @@ ProjDataFromStream::get_segment_by_sinogram(const int segment_num) const
{
error("ProjDataFromStream::get_segment_by_sinogram: error in stream state before reading\n");
}
-
- streamoff segment_offset = get_offset_segment(segment_num);
- if (get_storage_order() == Segment_AxialPos_View_TangPos)
+
+ if (get_storage_order() == Segment_AxialPos_View_TangPos || get_storage_order() == Timing_Segment_AxialPos_View_TangPos)
{
- SegmentBySinogram segment(proj_data_info_sptr,segment_num);
+ SegmentBySinogram segment(proj_data_info_sptr,segment_num, timing_num);
float scale = float(1);
Succeeded succeeded = Succeeded::yes;
+ const Bin bin(segment_num, this->get_min_view_num(), this->get_min_axial_pos_num(segment_num), this->get_min_tangential_pos_num(), timing_num);
#ifdef STIR_OPENMP
#pragma omp critical(PROJDATAFROMSTREAMIO)
#endif
- {
- sino_stream->seekg(segment_offset, ios::beg);
- if (! *sino_stream)
- {
- warning("ProjDataFromStream::get_segment_by_sinogram: error after seekg");
- succeeded = Succeeded::no;
- }
- else
- {
- succeeded = read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order);
- }
- } // end of critical section
+ try
+ {
+ detail::checked_seekg("get_segment_by_sinogram", *sino_stream, get_offset(bin));
+ succeeded = read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order);
+ }
+ catch (...)
+ {
+ succeeded = Succeeded::no;
+ }
+ // end of critical section
if (succeeded == Succeeded::no)
error("ProjDataFromStream: error reading data\n");
if(scale != 1)
@@ -891,14 +723,13 @@ ProjDataFromStream::get_segment_by_sinogram(const int segment_num) const
else
{
// TODO rewrite in terms of get_viewgram
- return SegmentBySinogram (get_segment_by_view(segment_num));
+ return SegmentBySinogram (get_segment_by_view(segment_num, timing_num));
}
}
SegmentByView
-ProjDataFromStream::get_segment_by_view(const int segment_num) const
+ProjDataFromStream::get_segment_by_view(const int segment_num, const int timing_pos) const
{
-
if(is_null_ptr(sino_stream))
{
error("ProjDataFromStream::get_segment_by_view: stream ptr is 0\n");
@@ -907,27 +738,26 @@ ProjDataFromStream::get_segment_by_view(const int segment_num) const
{
error("ProjDataFromStream::get_segment_by_view: error in stream state before reading\n");
}
- if (get_storage_order() == Segment_View_AxialPos_TangPos)
+
+ if (get_storage_order() == Segment_View_AxialPos_TangPos || get_storage_order() == Timing_Segment_View_AxialPos_TangPos)
{
- SegmentByView segment(proj_data_info_sptr,segment_num);
- streamoff segment_offset = get_offset_segment(segment_num);
+ SegmentByView segment(proj_data_info_sptr,segment_num, timing_pos);
float scale = float(1);
Succeeded succeeded = Succeeded::yes;
+ const Bin bin(segment_num, this->get_min_view_num(), this->get_min_axial_pos_num(segment_num), this->get_min_tangential_pos_num(), timing_pos);
#ifdef STIR_OPENMP
#pragma omp critical(PROJDATAFROMSTREAMIO)
#endif
- {
- sino_stream->seekg(segment_offset, ios::beg);
- if (! *sino_stream)
- {
- warning("ProjDataFromStream::get_segment_by_sinogram: error after seekg");
- succeeded = Succeeded::no;
- }
- else
- {
- succeeded = read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order);
- }
- } // end of critical section
+ try
+ {
+ detail::checked_seekg("get_segment_by_view", *sino_stream, get_offset(bin));
+ succeeded = read_data(*sino_stream, segment, on_disk_data_type, scale, on_disk_byte_order);
+ }
+ catch (...)
+ {
+ succeeded = Succeeded::no;
+ }
+ // end of critical section
if (succeeded == Succeeded::no)
error("ProjDataFromStream: error reading data");
if(scale != 1)
@@ -936,9 +766,9 @@ ProjDataFromStream::get_segment_by_view(const int segment_num) const
segment *= scale_factor;
return segment;
}
- else
+ else
// TODO rewrite in terms of get_sinogram as this doubles memory temporarily
- return SegmentByView (get_segment_by_sinogram(segment_num));
+ return SegmentByView (get_segment_by_sinogram(segment_num, timing_pos));
}
Succeeded
@@ -952,23 +782,22 @@ ProjDataFromStream::set_segment(const SegmentBySinogram& segmentbysinogra
{
error("ProjDataFromStream::set_segment: error in stream state before writing\n");
}
-
+
if (get_num_tangential_poss() != segmentbysinogram_v.get_num_tangential_poss())
{
- warning("ProjDataFromStream::set_segmen: num_bins is not correct\n");
+ warning("ProjDataFromStream::set_segmen: num_bins is not correct\n");
return Succeeded::no;
}
if (get_num_views() != segmentbysinogram_v.get_num_views())
{
- warning("ProjDataFromStream::set_segment: num_views is not correct\n");
+ warning("ProjDataFromStream::set_segment: num_views is not correct\n");
return Succeeded::no;
}
-
- int segment_num = segmentbysinogram_v.get_segment_num();
- streamoff segment_offset = get_offset_segment(segment_num);
-
-
- if (get_storage_order() == Segment_AxialPos_View_TangPos)
+
+ const int segment_num = segmentbysinogram_v.get_segment_num();
+ const Bin bin(segment_num, this->get_min_view_num(), this->get_min_axial_pos_num(segment_num), this->get_min_tangential_pos_num(), segmentbysinogram_v.get_timing_pos_num());
+
+ if (get_storage_order() == Segment_AxialPos_View_TangPos || get_storage_order() == Timing_Segment_AxialPos_View_TangPos)
{
// KT 03/07/2001 handle scale_factor appropriately
if (on_disk_data_type.id != NumericType::FLOAT)
@@ -982,25 +811,26 @@ ProjDataFromStream::set_segment(const SegmentBySinogram& segmentbysinogra
#ifdef STIR_OPENMP
#pragma omp critical(PROJDATAFROMSTREAMIO)
#endif
- {
- sino_stream->seekp(segment_offset,ios::beg);
- if (! *sino_stream)
- {
- warning("ProjDataFromStream::set_segment: error after seekp\n");
- succeeded = Succeeded::no;
- }
- else if (write_data(*sino_stream, segmentbysinogram_v, on_disk_data_type, scale, on_disk_byte_order)
- == Succeeded::no
- || scale != scale_factor)
- {
- warning("ProjDataFromStream::set_segment: segment (%d)"
- " corrupted due to problems with writing or the scale factor \n",
- segment_num);
- succeeded = Succeeded::no;
- }
- // flush the stream, see the class documentation
- sino_stream->flush();
- } // end of critical section
+ try
+ {
+ detail::checked_seekp("set_segment", *sino_stream, get_offset(bin));
+ if (write_data(*sino_stream, segmentbysinogram_v, on_disk_data_type, scale, on_disk_byte_order)
+ == Succeeded::no
+ || scale != scale_factor)
+ {
+ warning("ProjDataFromStream::set_segment: segment (%d) tof bin (%d)"
+ " corrupted due to problems with writing or the scale factor \n",
+ segment_num, segmentbysinogram_v.get_timing_pos_num());
+ succeeded = Succeeded::no;
+ }
+ // flush the stream, see the class documentation
+ sino_stream->flush();
+ }
+ catch (...)
+ {
+ succeeded = Succeeded::no;
+ }
+ // end of critical section
return succeeded;
}
else
@@ -1010,7 +840,7 @@ ProjDataFromStream::set_segment(const SegmentBySinogram& segmentbysinogra
SegmentByView(segmentbysinogram_v);
return set_segment(segmentbyview);
- }
+ }
}
@@ -1022,26 +852,26 @@ ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v)
error("ProjDataFromStream::set_segment: stream ptr is 0\n");
}
if (! *sino_stream)
- {
+ {
error("ProjDataFromStream::set_segment: error in stream state before writing\n");
}
-
-
+
+
if (get_num_tangential_poss() != segmentbyview_v.get_num_tangential_poss())
{
- warning("ProjDataFromStream::set_segment: num_bins is not correct\n");
+ warning("ProjDataFromStream::set_segment: num_bins is not correct\n");
return Succeeded::no;
}
if (get_num_views() != segmentbyview_v.get_num_views())
{
- warning("ProjDataFromStream::set_segment: num_views is not correct\n");
+ warning("ProjDataFromStream::set_segment: num_views is not correct\n");
return Succeeded::no;
}
-
- int segment_num = segmentbyview_v.get_segment_num();
- streamoff segment_offset = get_offset_segment(segment_num);
-
- if (get_storage_order() == Segment_View_AxialPos_TangPos)
+
+ const int segment_num = segmentbyview_v.get_segment_num();
+ const Bin bin(segment_num, this->get_min_view_num(), this->get_min_axial_pos_num(segment_num), this->get_min_tangential_pos_num(), segmentbyview_v.get_timing_pos_num());
+
+ if (get_storage_order() == Segment_View_AxialPos_TangPos || get_storage_order() == Timing_Segment_View_AxialPos_TangPos)
{
// KT 03/07/2001 handle scale_factor appropriately
if (on_disk_data_type.id != NumericType::FLOAT)
@@ -1055,25 +885,26 @@ ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v)
#ifdef STIR_OPENMP
#pragma omp critical(PROJDATAFROMSTREAMIO)
#endif
- {
- sino_stream->seekp(segment_offset,ios::beg);
- if (! *sino_stream)
- {
- warning("ProjDataFromStream::set_segment: error after seekp");
- succeeded = Succeeded::no;
- }
- else if (write_data(*sino_stream, segmentbyview_v, on_disk_data_type, scale, on_disk_byte_order)
- == Succeeded::no
- || scale != scale_factor)
- {
- warning("ProjDataFromStream::set_segment: segment (%d)"
- " corrupted due to problems with writing or the scale factor \n",
- segment_num);
- succeeded = Succeeded::no;
- }
- // flush the stream, see the class documentation
- sino_stream->flush();
- } // end of critical section
+ try
+ {
+ detail::checked_seekp("set_segment", *sino_stream, get_offset(bin));
+ if (write_data(*sino_stream, segmentbyview_v, on_disk_data_type, scale, on_disk_byte_order)
+ == Succeeded::no
+ || scale != scale_factor)
+ {
+ warning("ProjDataFromStream::set_segment: segment (%d) tof bin (%d)"
+ " corrupted due to problems with writing or the scale factor \n",
+ segment_num, segmentbyview_v.get_timing_pos_num());
+ succeeded = Succeeded::no;
+ }
+ // flush the stream, see the class documentation
+ sino_stream->flush();
+ }
+ catch (...)
+ {
+ succeeded = Succeeded::no;
+ }
+ // end of critical section
return succeeded;
}
else
@@ -1089,29 +920,29 @@ ProjDataFromStream::set_segment(const SegmentByView& segmentbyview_v)
#if 0
ProjDataFromStream* ProjDataFromStream::ask_parameters(const bool on_disk)
{
-
+
shared_ptr p_in_stream;
-
-
+
+
char filename[256];
-
+
ask_filename_with_extension(
- filename,
+ filename,
"Enter file name of 3D sinogram data : ", ".scn");
// KT 03/07/2001 initialise to avoid compiler warnings
- ios::openmode open_mode=ios::in;
+ ios::openmode open_mode=ios::in;
switch(ask_num("Read (1), Create and write(2), Read/Write (3) : ", 1,3,1))
{
case 1: open_mode=ios::in; break;
case 2: open_mode=ios::out; break;
case 3: open_mode=ios::in | ios::out; break;
}
-
+
if (on_disk)
{
-
+
//fstream * p_fstream = new fstream;
p_in_stream.reset(new fstream (filename, open_mode | ios::binary));
if (!p_in_stream->good())
@@ -1122,48 +953,48 @@ ProjDataFromStream* ProjDataFromStream::ask_parameters(const bool on_disk)
//p_in_stream = p_fstream;
}
else
- {
+ {
streamsize file_size = 0;
char *memory = 0;
- {
+ {
fstream input;
open_read_binary(input, filename);
memory = (char *)read_stream_in_memory(input, file_size);
}
-
+
#ifdef BOOST_NO_STRINGSTREAM
// This is the old implementation of the strstream class.
// The next constructor should work according to the doc, but it doesn't in gcc 2.8.1.
//strstream in_stream(memory, file_size, ios::in | ios::binary);
- // Reason: in_stream contains an internal strstreambuf which is
+ // Reason: in_stream contains an internal strstreambuf which is
// initialised as buffer(memory, file_size, memory), which prevents
// reading from it.
-
+
strstreambuf * buffer = new strstreambuf(memory, file_size, memory+file_size);
p_in_stream.reset(new iostream(buffer));
#else
// TODO this does allocate and copy 2 times
// TODO file_size could be longer than what size_t allows, but string doesn't take anything longer
- p_in_stream.reset(new std::stringstream (string(memory, std::size_t(file_size)),
+ p_in_stream.reset(new std::stringstream (string(memory, std::size_t(file_size)),
open_mode | ios::binary));
-
+
delete[] memory;
#endif
-
- } // else 'on_disk'
-
- // KT 03/07/2001 initialise to avoid compiler warnings
+ } // else 'on_disk'
+
+
+ // KT 03/07/2001 initialise to avoid compiler warnings
ProjDataFromStream::StorageOrder storage_order =
Segment_AxialPos_View_TangPos;
{
int data_org = ask_num("Type of data organisation:\n\
- 0: Segment_AxialPos_View_TangPos, 1: Segment_View_AxialPos_TangPos",
+ 0: Segment_AxialPos_View_TangPos, 1: Segment_View_AxialPos_TangPos",
0,
1,0);
-
+
switch (data_org)
- {
+ {
case 0:
storage_order = ProjDataFromStream::Segment_AxialPos_View_TangPos;
break;
@@ -1172,13 +1003,13 @@ ProjDataFromStream* ProjDataFromStream::ask_parameters(const bool on_disk)
break;
}
}
-
+
NumericType data_type;
{
int data_type_sel = ask_num("Type of data :\n\
0: signed 16bit int, 1: unsigned 16bit int, 2: 4bit float ", 0,2,2);
switch (data_type_sel)
- {
+ {
case 0:
data_type = NumericType::SHORT;
break;
@@ -1190,80 +1021,80 @@ ProjDataFromStream* ProjDataFromStream::ask_parameters(const bool on_disk)
break;
}
}
-
-
+
+
ByteOrder byte_order;
- {
- byte_order =
+ {
+ byte_order =
ask("Little endian byte order ?",
ByteOrder::get_native_order() == ByteOrder::little_endian) ?
ByteOrder::little_endian :
ByteOrder::big_endian;
}
-
+
std::streamoff offset_in_file ;
{
// find file size
- p_in_stream->seekg(static_cast(0), ios::beg);
+ p_in_stream->seekg(static_cast(0), ios::beg);
streamsize file_size = find_remaining_size(*p_in_stream);
-
- offset_in_file = ask_num("Offset in file (in bytes)",
+
+ offset_in_file = ask_num("Offset in file (in bytes)",
static_cast(0),
- static_cast(file_size),
+ static_cast(file_size),
static_cast(0));
}
float scale_factor =1;
-
+
shared_ptr data_info_ptr(ProjDataInfo::ask_parameters());
-
- vector segment_sequence_in_stream;
- segment_sequence_in_stream = vector(data_info_ptr->get_num_segments());
- segment_sequence_in_stream[0] = 0;
-
+
+ vector segment_sequence_in_stream;
+ segment_sequence_in_stream = vector(data_info_ptr->get_num_segments());
+ segment_sequence_in_stream[0] = 0;
+
for (int i=1; i<= data_info_ptr->get_num_segments()/2; i++)
- {
+ {
segment_sequence_in_stream[2*i-1] = i;
segment_sequence_in_stream[2*i] = -i;
}
-
-
+
+
cerr << "Segment sequence :";
for (unsigned int i=0; i
ProjDataGEAdvance::
get_viewgram(const int view_num, const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd, const int timing_pos) const
{
// --------------------------------------------------------
// --------------------------------------------------------
@@ -306,7 +306,8 @@ Succeeded ProjDataGEAdvance::set_viewgram(const Viewgram& v)
return Succeeded::no;
}
-Sinogram ProjDataGEAdvance::get_sinogram(const int ax_pos_num, const int segment_num,const bool make_num_tangential_poss_odd) const
+Sinogram ProjDataGEAdvance::get_sinogram(const int ax_pos_num, const int segment_num,
+ const bool make_num_tangential_poss_odd, const int timing_pos) const
{
// TODO
error("ProjDataGEAdvance::get_sinogram not implemented yet\n");
diff --git a/src/buildblock/ProjDataGEHDF5.cxx b/src/buildblock/ProjDataGEHDF5.cxx
index 09a6bae9a..08f81a0ca 100644
--- a/src/buildblock/ProjDataGEHDF5.cxx
+++ b/src/buildblock/ProjDataGEHDF5.cxx
@@ -118,7 +118,8 @@ void ProjDataGEHDF5::initialise_ax_pos_offset()
Viewgram
ProjDataGEHDF5::
get_viewgram(const int view_num, const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos) const
{
if (make_num_tangential_poss_odd)
error("make_num_tangential_poss_odd not supported by ProjDataGEHDF5");
@@ -187,7 +188,8 @@ Succeeded ProjDataGEHDF5::set_viewgram(const Viewgram& v)
return Succeeded::no;
}
-Sinogram ProjDataGEHDF5::get_sinogram(const int ax_pos_num, const int segment_num,const bool make_num_tangential_poss_odd) const
+Sinogram ProjDataGEHDF5::get_sinogram(const int ax_pos_num, const int segment_num,const bool make_num_tangential_poss_odd,
+ const int timing_pos) const
{
// TODO
error("ProjDataGEHDF5::get_sinogram not implemented yet");
diff --git a/src/buildblock/ProjDataInMemory.cxx b/src/buildblock/ProjDataInMemory.cxx
index 34d8404c3..9c6f0aa72 100644
--- a/src/buildblock/ProjDataInMemory.cxx
+++ b/src/buildblock/ProjDataInMemory.cxx
@@ -4,11 +4,13 @@
\ingroup projdata
\brief Implementations for non-inline functions of class stir::ProjDataInMemory
+ \author Nikos Efthimiou
\author Kris Thielemans
\author Nikos Efthimiou
\author Gemma Fardell
*/
/*
+ * Copyright (C) 2016, UCL
Copyright (C) 2002 - 2011-02-23, Hammersmith Imanet Ltd
Copyright (C) 2011, Kris Thielemans
Copyright (C) 2016, University of Hull
diff --git a/src/buildblock/ProjDataInfo.cxx b/src/buildblock/ProjDataInfo.cxx
index 0a0823dc8..1189d6ce2 100644
--- a/src/buildblock/ProjDataInfo.cxx
+++ b/src/buildblock/ProjDataInfo.cxx
@@ -4,8 +4,9 @@
Copyright (C) 2000 PARAPET partners
Copyright (C) 2000 - 2009-05-13, Hammersmith Imanet Ltd
Copyright (C) 2011-07-01 - 2011, Kris Thielemans
- Copyright (C) 2018, 2022, 2023, University College London
Copyright (C) 2018, University of Leeds
+ Copyright (C) 2018, 2020-2023 University College London
+ Copyright (C) 2016-2019, University of Hull
This file is part of STIR.
SPDX-License-Identifier: Apache-2.0 AND License-ref-PARAPET-license
@@ -17,6 +18,7 @@
\brief Implementation of non-inline functions of class stir::ProjDataInfo
+ \author Nikos Efthimiou
\author Sanida Mustafovic
\author Kris Thielemans
\author PARAPET project
@@ -53,6 +55,8 @@
#else
#include
#endif
+#include "stir/info.h"
+#include "boost/foreach.hpp"
#include "boost/format.hpp"
#ifndef STIR_NO_NAMESPACES
@@ -66,6 +70,30 @@ using std::equal;
START_NAMESPACE_STIR
+float
+ProjDataInfo::get_k(const Bin& bin) const
+{
+ if (!(num_tof_bins%2))
+ return bin.timing_pos_num() * tof_increament_in_mm + tof_increament_in_mm / 2.f;
+ else
+ return (bin.timing_pos_num() * tof_increament_in_mm);
+}
+
+double
+ProjDataInfo::get_tof_delta_time(const Bin& bin) const
+{
+ return mm_to_tof_delta_time(get_k(bin));
+}
+
+float
+ProjDataInfo::get_sampling_in_k(const Bin& bin) const
+{
+ return (get_k(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num(),bin.tangential_pos_num(),
+ bin.timing_pos_num()+1)) -
+ get_k(Bin(bin.segment_num(), bin.view_num(), bin.axial_pos_num(),bin.tangential_pos_num(),
+ bin.timing_pos_num()-1))
+ )/2.f;
+}
float
ProjDataInfo::get_sampling_in_t(const Bin& bin) const
@@ -155,10 +183,91 @@ ProjDataInfo::set_max_tangential_pos_num(const int max_tang_poss)
max_tangential_pos_num = max_tang_poss;
}
+//! \todo N.E: This function is very ugly and unnessesary complicated. Could be much better.
void
-ProjDataInfo::set_tof_mash_factor(const int)
+ProjDataInfo::set_tof_mash_factor(const int new_num)
{
- warning("TOF support not yet enabled.");
+ if (scanner_ptr->is_tof_ready() && new_num > 0 )
+ {
+ tof_mash_factor = new_num;
+ if(tof_mash_factor > scanner_ptr->get_max_num_timing_poss())
+ error("ProjDataInfo::set_tof_mash_factor: TOF mashing factor ("
+ + std::to_string(tof_mash_factor) +
+ + ") must be smaller than or equal to the scanner's number of max timing bins ("
+ + std::to_string(scanner_ptr->get_max_num_timing_poss())
+ + ").");
+
+#if 0
+ // KT: code disabled as buggy but currently not needed
+ tof_increament_in_mm = tof_delta_time_to_mm(scanner_ptr->get_size_of_timing_pos());
+ min_unmashed_tof_pos_num = - (scanner_ptr->get_max_num_timing_poss())/2;
+ max_unmashed_tof_pos_num = min_unmashed_tof_pos_num + (scanner_ptr->get_max_num_timing_poss()) -1;
+
+ // Upper and lower boundaries of the timing poss;
+ tof_bin_unmashed_boundaries_mm.grow(min_unmashed_tof_pos_num, max_unmashed_tof_pos_num);
+ tof_bin_unmashed_boundaries_ps.grow(min_unmashed_tof_pos_num, max_unmashed_tof_pos_num);
+
+ // Silently intialise the unmashed TOF bins.
+ for (int k = min_unmashed_tof_pos_num; k <= max_unmashed_tof_pos_num; ++k )
+ {
+ Bin bin;
+ bin.timing_pos_num() = k;
+ // if we ever re-enable this code, there is a BUG here:
+ // get_k relies on num_tof_bins, so this should have been set to the unmashed value from the scanner
+ float cur_low = get_k(bin) - get_sampling_in_k(bin)/2.f;
+ float cur_high = get_k(bin) + get_sampling_in_k(bin)/2.f;
+
+ tof_bin_unmashed_boundaries_mm[k].low_lim = cur_low;
+ tof_bin_unmashed_boundaries_mm[k].high_lim = cur_high;
+ tof_bin_unmashed_boundaries_ps[k].low_lim = static_cast(mm_to_tof_delta_time(tof_bin_unmashed_boundaries_mm[k].low_lim));
+ tof_bin_unmashed_boundaries_ps[k].high_lim = static_cast(mm_to_tof_delta_time(tof_bin_unmashed_boundaries_mm[k].high_lim));
+
+ }
+#endif
+ // Now, initialise the mashed TOF bins.
+ tof_increament_in_mm = tof_delta_time_to_mm(tof_mash_factor * scanner_ptr->get_size_of_timing_pos());
+
+ // TODO cope with even numbers!
+ min_tof_pos_num = - (scanner_ptr->get_max_num_timing_poss() / tof_mash_factor)/2;
+ max_tof_pos_num = min_tof_pos_num + (scanner_ptr->get_max_num_timing_poss() / tof_mash_factor) -1;
+
+ num_tof_bins = max_tof_pos_num - min_tof_pos_num +1 ;
+
+ // Ensure that we have a central tof bin.
+ if (num_tof_bins%2 == 0)
+ error("ProjDataInfo: Number of TOF bins should be an odd number. Abort.");
+
+ // Upper and lower boundaries of the timing poss;
+ tof_bin_boundaries_mm.grow(min_tof_pos_num, max_tof_pos_num);
+
+ tof_bin_boundaries_ps.grow(min_tof_pos_num, max_tof_pos_num);
+
+ for (int k = min_tof_pos_num; k <= max_tof_pos_num; ++k )
+ {
+ Bin bin;
+ bin.timing_pos_num() = k;
+
+ float cur_low = get_k(bin) - get_sampling_in_k(bin)/2.f;
+ float cur_high = get_k(bin) + get_sampling_in_k(bin)/2.f;
+
+ tof_bin_boundaries_mm[k].low_lim = cur_low;
+ tof_bin_boundaries_mm[k].high_lim = cur_high;
+ tof_bin_boundaries_ps[k].low_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[k].low_lim));
+ tof_bin_boundaries_ps[k].high_lim = static_cast(mm_to_tof_delta_time(tof_bin_boundaries_mm[k].high_lim));
+ // I could imagine a better printing.
+ info(boost::format("Tbin %1%: %2% - %3% mm (%4% - %5% ps) = %6%") %k % tof_bin_boundaries_mm[k].low_lim % tof_bin_boundaries_mm[k].high_lim
+ % tof_bin_boundaries_ps[k].low_lim % tof_bin_boundaries_ps[k].high_lim % get_sampling_in_k(bin));
+ }
+ }
+ else if ((scanner_ptr->is_tof_ready() && new_num <= 0)
+ || !scanner_ptr->is_tof_ready()) // Case new_num <=, will produce non-TOF data for a TOF compatible scanner
+ {
+ num_tof_bins = 1;
+ tof_mash_factor = 0;
+ min_tof_pos_num = 0;
+ max_tof_pos_num = 0;
+ // we assume TOF mashing factor = 0 means non-TOF and the projecter won't use any boundary conditions
+ }
}
std::vector
@@ -189,25 +298,42 @@ ProjDataInfo::ProjDataInfo(const shared_ptr& scanner_ptr_v,
set_num_views(num_views_v);
set_num_tangential_poss(num_tangential_poss_v);
set_num_axial_poss_per_segment(num_axial_pos_per_segment_v);
+ // Initialise the TOF elements to non-used.
+ min_tof_pos_num = 0;
+ max_tof_pos_num = 0;
+ tof_increament_in_mm = 0.f;
+ tof_mash_factor = 0;
+ num_tof_bins = 1;
+}
+
+// TOF version.
+ProjDataInfo::ProjDataInfo(const shared_ptr& scanner_ptr_v,
+ const VectorWithOffset& num_axial_pos_per_segment_v,
+ const int num_views_v,
+ const int num_tangential_poss_v,
+ const int tof_mash_factor_v)
+ :scanner_ptr(scanner_ptr_v)
+
+{
+ set_tof_mash_factor(tof_mash_factor_v);
+ set_num_views(num_views_v);
+ set_num_tangential_poss(num_tangential_poss_v);
+ set_num_axial_poss_per_segment(num_axial_pos_per_segment_v);
}
string
ProjDataInfo::parameter_info() const
{
-#ifdef BOOST_NO_STRINGSTREAM
- // dangerous for out-of-range, but 'old-style' ostrstream seems to need this
- char str[30000];
- ostrstream s(str, 30000);
-#else
std::ostringstream s;
-#endif
s << scanner_ptr->parameter_info();
s << "\n";
s << "start vertical bed position (mm) := "
<< get_bed_position_vertical() << endl;
s << "start horizontal bed position (mm) := "
<< get_bed_position_horizontal() << endl;
+ s << "\nTOF mashing factor in data: " << get_tof_mash_factor() << '\n';
+ s << "Number of TOF positions in data: " << get_num_tof_poss() << '\n';
s << "\nSegment_num range: ("
<< get_min_segment_num()
<< ", " << get_max_segment_num() << ")\n";
@@ -249,7 +375,8 @@ reduce_segment_range(const int min_segment_num, const int max_segment_num)
Viewgram
ProjDataInfo::get_empty_viewgram(const int view_num,
const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos_num) const
{
// we can't access the shared ptr, so we have to clone 'this'.
shared_ptr proj_data_info_sptr(this->clone());
@@ -257,7 +384,7 @@ ProjDataInfo::get_empty_viewgram(const int view_num,
if (make_num_tangential_poss_odd && (get_num_tangential_poss()%2==0))
proj_data_info_sptr->set_max_tangential_pos_num(get_max_tangential_pos_num() + 1);
- Viewgram v(proj_data_info_sptr, view_num, segment_num);
+ Viewgram v(proj_data_info_sptr, view_num, segment_num, timing_pos_num);
return v;
}
@@ -274,7 +401,8 @@ ProjDataInfo::get_empty_viewgram(const ViewgramIndices& ind) const
Sinogram
ProjDataInfo::get_empty_sinogram(const int axial_pos_num, const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos_num) const
{
// we can't access the shared ptr, so we have to clone 'this'.
shared_ptr proj_data_info_sptr(this->clone());
@@ -282,7 +410,7 @@ ProjDataInfo::get_empty_sinogram(const int axial_pos_num, const int segment_num,
if (make_num_tangential_poss_odd && (get_num_tangential_poss()%2==0))
proj_data_info_sptr->set_max_tangential_pos_num(get_max_tangential_pos_num() + 1);
- Sinogram s(proj_data_info_sptr, axial_pos_num, segment_num);
+ Sinogram s(proj_data_info_sptr, axial_pos_num, segment_num, timing_pos_num);
return s;
}
@@ -298,7 +426,8 @@ ProjDataInfo::get_empty_sinogram(const SinogramIndices& ind) const
SegmentBySinogram
ProjDataInfo::get_empty_segment_by_sinogram(const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos_num) const
{
assert(segment_num >= get_min_segment_num());
assert(segment_num <= get_max_segment_num());
@@ -309,7 +438,7 @@ ProjDataInfo::get_empty_segment_by_sinogram(const int segment_num,
if (make_num_tangential_poss_odd && (get_num_tangential_poss()%2==0))
proj_data_info_sptr->set_max_tangential_pos_num(get_max_tangential_pos_num() + 1);
- SegmentBySinogram s(proj_data_info_sptr, segment_num);
+ SegmentBySinogram s(proj_data_info_sptr, segment_num, timing_pos_num);
return s;
}
@@ -325,7 +454,8 @@ ProjDataInfo::get_empty_segment_by_sinogram(const SegmentIndices& ind) const
SegmentByView
ProjDataInfo::get_empty_segment_by_view(const int segment_num,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos_num) const
{
assert(segment_num >= get_min_segment_num());
assert(segment_num <= get_max_segment_num());
@@ -336,7 +466,7 @@ ProjDataInfo::get_empty_segment_by_view(const int segment_num,
if (make_num_tangential_poss_odd && (get_num_tangential_poss()%2==0))
proj_data_info_sptr->set_max_tangential_pos_num(get_max_tangential_pos_num() + 1);
- SegmentByView s(proj_data_info_sptr, segment_num);
+ SegmentByView s(proj_data_info_sptr, segment_num, timing_pos_num);
return s;
}
@@ -353,7 +483,8 @@ ProjDataInfo::get_empty_segment_by_view(const SegmentIndices& ind) const
RelatedViewgrams
ProjDataInfo::get_empty_related_viewgrams(const ViewgramIndices& viewgram_indices,
const shared_ptr& symmetries_used,
- const bool make_num_tangential_poss_odd) const
+ const bool make_num_tangential_poss_odd,
+ const int timing_pos_num) const
{
if (make_num_tangential_poss_odd)
error("make_num_tangential_poss_odd is no longer supported");
@@ -365,6 +496,8 @@ ProjDataInfo::get_empty_related_viewgrams(const ViewgramIndices& viewgram_indice
for (unsigned int i=0; i& scanner,
- const int span, const int max_delta,
- const int num_views, const int num_tangential_poss,
- const bool arc_corrected)
+ const int span, const int max_delta,
+ const int num_views, const int num_tangential_poss,
+ const bool arc_corrected,
+ const int tof_mash_factor)
{
const int num_ring = scanner->get_num_rings();
if (max_delta > num_ring - 1)
@@ -486,32 +620,37 @@ ProjDataInfo::ProjDataInfoCTI(const shared_ptr& scanner,
num_axial_pos_per_segment,
min_ring_difference,
max_ring_difference,
- num_views,num_tangential_poss);
+ num_views,num_tangential_poss,
+ tof_mash_factor);
else
return
new ProjDataInfoCylindricalNoArcCorr(scanner,
- num_axial_pos_per_segment,
- min_ring_difference,
- max_ring_difference,
- num_views,num_tangential_poss);
+ num_axial_pos_per_segment,
+ min_ring_difference,
+ max_ring_difference,
+ num_views,num_tangential_poss,
+ tof_mash_factor);
}
unique_ptr
ProjDataInfo::construct_proj_data_info(const shared_ptr& scanner_sptr,
const int span, const int max_delta,
const int num_views, const int num_tangential_poss,
- const bool arc_corrected)
+ const bool arc_corrected,
+ const int tof_mash_factor)
{
- unique_ptr pdi(ProjDataInfoCTI(scanner_sptr, span, max_delta, num_views, num_tangential_poss, arc_corrected));
+ unique_ptr pdi(ProjDataInfoCTI(scanner_sptr, span, max_delta, num_views, num_tangential_poss, arc_corrected, tof_mash_factor));
return pdi;
}
// KT 28/06/2001 added arc_corrected flag
+// NE 28/12/2016 added the tof_mash_factor
ProjDataInfo*
ProjDataInfo::ProjDataInfoGE(const shared_ptr& scanner,
- const int max_delta,
- const int num_views, const int num_tangential_poss,
- const bool arc_corrected)
+ const int max_delta,
+ const int num_views, const int num_tangential_poss,
+ const bool arc_corrected,
+ const int tof_mash_factor)
{
@@ -558,14 +697,16 @@ ProjDataInfo::ProjDataInfoGE(const shared_ptr& scanner,
num_axial_pos_per_segment,
min_ring_difference,
max_ring_difference,
- num_views,num_tangential_poss);
+ num_views,num_tangential_poss,
+ tof_mash_factor);
else
return
new ProjDataInfoCylindricalNoArcCorr(scanner,
num_axial_pos_per_segment,
min_ring_difference,
max_ring_difference,
- num_views,num_tangential_poss);
+ num_views,num_tangential_poss,
+ tof_mash_factor);
}
@@ -574,19 +715,15 @@ ProjDataInfo* ProjDataInfo::ask_parameters()
shared_ptr