Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TOF interfile and scanner fixes #66

32 changes: 25 additions & 7 deletions documentation/release_notes_TOF.htm
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,30 @@ <h3>New functionality</h3>

<h3>Changed functionality</h3>
<ul>
<li>
<li>
We now always check (<code>ProjDataInfo*NoArcCorr</code>) 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.
</li>
</li>
<li>
Write <code>STIR6.0</code> as Interfile key version to denote TOF changes.
This is currently ignored for parsing though.
</li>
</ul>

<h3>Changed functionality breaking backwards incompatibility</h3>
<ul>
<li><code>virtual ListModeData::get_scanner_ptr()</code> is replaced by <code>ListModeData::get_scanner()</code>.
</li>
<li>
<code>ProjDataInfo::ask_parameters()</code> and therefore <code>create_projdata_template</code>
has changed:
<ol>
<li>If the scanner definition in STIR has TOF capabilities, it will ask for the TOF mashing factor.</li>
<li>The default for arc-correction has changed to <i>N</i>, i.e. <code>false</code>.</li>
<li>Default value for span is now 11 for Siemens and 2 for GE scanners.</li>
<li>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.<br />
(Reading a "span=0" case is still supported")</li>
</ol>
</li>
</ul>


Expand Down Expand Up @@ -118,9 +132,13 @@ <h3>Major bugs fixed</h3>

<h3>Backward incompatibities</h3>
<ul>
<li><code>ProjDataInfo*NoArcCorr::get_bin_for_det_pair</code> is now private.
Use <code>get_bin_for_det_pos_pair</code> instead.
</li>
<li>
<code>virtual ListModeData::get_scanner_ptr()</code> is replaced by <code>ListModeData::get_scanner()</code>.
</li>
<li>
<code>ProjDataInfo*NoArcCorr::get_bin_for_det_pair</code> is now private.
Use <code>get_bin_for_det_pos_pair</code> instead.
</li>
</ul>

<h3>New functionality</h3>
Expand Down
62 changes: 62 additions & 0 deletions examples/samples/PET_TOF_Interfile_header_Signa_PETMR.hs
Original file line number Diff line number Diff line change
@@ -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 :=
Empty file.
6 changes: 3 additions & 3 deletions recon_test_pack/run_test_simulate_and_recon_with_motion.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<EOF
Discovery STE

1
n

0
2
3
EOF
create_projdata_template ${template_sino} < my_input.txt > my_create_${template_sino}.log 2>&1
if [ $? -ne 0 ]; then
Expand Down
9 changes: 4 additions & 5 deletions recon_test_pack/run_test_zoom_image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +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 <<EOF
Discovery STE

1
410
n

0
2

3
EOF
create_projdata_template ${template_sino} < my_input.txt > my_create_${template_sino}.log 2>&1
if [ $? -ne 0 ]; then
Expand Down
3 changes: 1 addition & 2 deletions recon_test_pack/simulate_PET_data_for_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,11 @@ echo "=== make attenuation image"
generate_image generate_atten_cylinder.par
if [ "$TOF" -eq 0 ]; then
echo "=== create template sinogram (DSTE in 3D with max ring diff 2 to save time)"
template_sino=my_DSTE_3D_rd2_template.hs
template_sino=my_DSTE_3D_rd3_template.hs
cat > my_input.txt <<EOF
Discovery STE

1
0
n

0
Expand Down
47 changes: 23 additions & 24 deletions recon_test_pack/template_for_ROOT_scanner.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,30 +27,29 @@ matrix axis label [1] := tangential coordinate
!matrix size [1] := 501
minimum ring difference per segment := { -3,-2,-1,0,1,2,3}
maximum ring difference per segment := { -3,-2,-1,0,1,2,3}
;%TOF mashing factor := 1
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 := 501
Default number of arc-corrected bins := 501
Energy resolution := 0
Reference energy (in keV) := 511
Number of TOF time bins :=5
Size of timing bin (ps) := 820
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
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
Expand Down
34 changes: 19 additions & 15 deletions src/IO/InterfileHeader.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,12 @@ InterfilePDFSHeader::InterfilePDFSHeader()
add_alias_key("Size of unmashed TOF time bins (ps)", "Size of timing bin (ps)");
#endif
timing_resolution = -1.f;
add_key("Timing resolution (ps)",
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)",
Expand Down Expand Up @@ -1363,23 +1367,23 @@ bool InterfilePDFSHeader::post_processing()
if (guessed_scanner_ptr->is_tof_ready())
{
if (max_num_timing_poss != guessed_scanner_ptr->get_max_num_timing_poss())
{
warning("Interfile warning: 'Number of TOF time bins' (%d) is expected to be %d.",
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 (size_of_timing_pos != guessed_scanner_ptr->get_size_of_timing_pos())
{
warning("Interfile warning: 'Size of timing bin (ps)' (%f) is expected to be %f.",
size_of_timing_pos, guessed_scanner_ptr->get_size_of_timing_pos());
}
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 (timing_resolution != guessed_scanner_ptr->get_timing_resolution())
{
warning("Interfile warning: 'Timing resolution (ps)' (%f) is expected to be %f.",
timing_resolution, guessed_scanner_ptr->get_timing_resolution());
}
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
Expand Down
22 changes: 8 additions & 14 deletions src/IO/interfile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1209,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";
Expand Down Expand Up @@ -1302,9 +1302,8 @@ write_basic_interfile_PDFS_header(const string& header_file_name,

// it's PET data if we get here
// N.E. Added timing locations
pdfs.get_proj_data_info_sptr()->get_num_tof_poss()>1 ?
output_header << "number of dimensions := 5\n" :
output_header << "number of dimensions := 4\n";
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";

// TODO support more ?
{
Expand Down Expand Up @@ -1386,15 +1385,10 @@ write_basic_interfile_PDFS_header(const string& header_file_name,
output_header << "!matrix size [" << order_of_bin << "] := "
<<pdfs.get_proj_data_info_sptr()->get_num_tangential_poss() << "\n";

// If TOF is supported; add this in the header.
if (pdfs.get_proj_data_info_sptr()->get_scanner_ptr()->is_tof_ready() &&
pdfs.get_proj_data_info_sptr()->get_num_tof_poss() > 1)
if (is_TOF)
{
// Moved in scanner section
// output_header << "%number of TOF time bins :=" <<
// pdfs.get_proj_data_info_ptr()->get_scanner_ptr()->get_num_max_of_timing_bins() << "\n";
output_header << "%TOF mashing factor := " <<
pdfs.get_proj_data_info_sptr()->get_tof_mash_factor() << "\n";
output_header << "TOF mashing factor := " <<
pdfs.get_proj_data_info_sptr()->get_tof_mash_factor() << "\n";
}
}

Expand Down
29 changes: 11 additions & 18 deletions src/buildblock/ProjDataInfo.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -715,46 +715,39 @@ ProjDataInfo* ProjDataInfo::ask_parameters()

shared_ptr<Scanner> scanner_ptr(Scanner::ask_parameters());

const bool is_Advance =
scanner_ptr->get_type() == Scanner::Advance ||
scanner_ptr->get_type() == Scanner::DiscoveryLS;
const bool is_DiscoveryST =
scanner_ptr->get_type() == Scanner::DiscoveryST;
const bool is_GE =
is_Advance || is_DiscoveryST;

const int num_views = scanner_ptr->get_max_num_views()/
ask_num("Mash factor for views",1, scanner_ptr->get_max_num_views(),1);

const int tof_mash_factor = scanner_ptr->is_tof_ready() ?
ask_num("Time-of-flight mash factor:", 0,
scanner_ptr->get_max_num_timing_poss(), 25) : 0;
scanner_ptr->get_max_num_timing_poss(), 1) : 0;

const bool arc_corrected =
ask("Is the data arc-corrected?",true);
ask("Is the data arc-corrected?",false);

const int num_tangential_poss=
ask_num("Number of tangential positions",1,
scanner_ptr->get_max_num_non_arccorrected_bins(),
arc_corrected
? scanner_ptr->get_default_num_arccorrected_bins()
: scanner_ptr->get_max_num_non_arccorrected_bins());

int span = is_GE ? 0 : 1;

const bool is_GE = scanner_ptr->get_name().substr(0,2) == "GE";
const bool is_Siemens = scanner_ptr->get_name().substr(0,6) == "Siemens";
int span = is_GE ? 2 : (is_Siemens ? 11 : 1);
span =
ask_num("Span value (use 0 for mixed-span case of the Advance) : ", 0,scanner_ptr->get_num_rings()-1,span);
ask_num("Span value : ", 0,scanner_ptr->get_num_rings()-1,span);


const int max_delta = ask_num("Max. ring difference acquired : ",
0,
scanner_ptr->get_num_rings()-1,
is_Advance ? 11 : scanner_ptr->get_num_rings()-1);

scanner_ptr->get_num_rings()-1);

ProjDataInfo * pdi_ptr =
ProjDataInfo * pdi_ptr =
span==0
? ProjDataInfoGE(scanner_ptr,max_delta,num_views,num_tangential_poss,arc_corrected, tof_mash_factor)
: ProjDataInfoCTI(scanner_ptr,span,max_delta,num_views,num_tangential_poss,arc_corrected, tof_mash_factor);
? ProjDataInfoGE(scanner_ptr,max_delta,num_views,num_tangential_poss,arc_corrected, tof_mash_factor)
: ProjDataInfoCTI(scanner_ptr,span,max_delta,num_views,num_tangential_poss,arc_corrected, tof_mash_factor);

cout << pdi_ptr->parameter_info() <<endl;

Expand Down
Loading